Handy_Cao 1 年之前
父节点
当前提交
6a2cbb0c71

+ 37 - 0
public/config/columns.json

@@ -37,6 +37,43 @@
         ]
     },
     {
+        "tableKey": "order-detai2",
+        "columns": [
+            {
+                "field": "marketname",
+                "label": "position.goods.holddetail.marketname"
+            },
+            {
+                "field": "goodsname",
+                "label": "position.goods.goodsname"
+            },
+            {
+                "field": "buyorsell",
+                "label": "position.goods.buyorsell"
+            },
+            {
+                "field": "holderqty",
+                "label": "position.goods.holddetail.holderqty"
+            },
+            {
+                "field": "holderprice",
+                "label": "position.goods.holddetail.holderprice"
+            },
+            {
+                "field": "holderamount",
+                "label": "position.goods.holddetail.holderamount"
+            },
+            {
+                "field": "tradetime",
+                "label": "position.goods.holddetail.tradetime"
+            },
+            {
+                "field": "tradeid",
+                "label": "position.goods.holddetail.tradeid"
+            }
+        ]
+    },
+    {
         "tableKey": "transfer-position",
         "columns": [
             {

+ 16 - 2
public/config/router.json

@@ -176,17 +176,31 @@
                     },
                     {
                         "authType": 2,
-                        "sort": 2,
+                        "sort": 3,
+                        "title": "pcroute.bottom.bottom_pricing_detail",
+                        "code": "bottom_pricing_detail2",
+                        "component": "views/footer/pricing/detail2/index.vue"
+                    },
+                    {
+                        "authType": 2,
+                        "sort": 4,
                         "title": "pcroute.bottom.bottom_pricing_order",
                         "code": "bottom_pricing_order",
                         "component": "views/footer/pricing/order/index.vue"
                     },
                     {
                         "authType": 2,
-                        "sort": 3,
+                        "sort": 5,
                         "title": "pcroute.bottom.bottom_pricing_trade",
                         "code": "bottom_pricing_trade",
                         "component": "views/footer/pricing/trade/index.vue"
+                    },
+                    {
+                        "authType": 2,
+                        "sort": 6,
+                        "title": "pcroute.bottom.bottom_goods_delivery",
+                        "code": "bottom_goods_delivery",
+                        "component": "views/footer/goods/delivery/index.vue"
                     }
                 ]
             },

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

@@ -886,7 +886,8 @@
             "reqtime": "ApplyTime",
             "applydate": "ApplyDate",
             "orderstatusdisplay": "OrderStatus",
-            "deliveryorderid": "OrderId"
+            "deliveryorderid": "OrderId",
+            "tradeid": "TradeID"
         },
         "spot": {
             "title": "Spot",
@@ -1541,6 +1542,7 @@
             "bottom_pricing_detail": "Hold Details",
             "bottom_pricing_order": "Order",
             "bottom_pricing_trade": "Trade",
+            "bottom_pricing_delivery": "Delivery",
             "bottom_swap": "Swap Market",
             "bottom_swap_position": "Position",
             "bottom_swap_order": "Order",

+ 3 - 1
public/locales/th-TH.json

@@ -890,7 +890,8 @@
             "reqtime": "เวลาของโปรแกรม",
             "applydate": "วัน ที่ของการสมัคร",
             "orderstatusdisplay": "สถานะการส่งเอกสาร",
-            "deliveryorderid": "หมายเลขใบเรียกเก็บเงิน"
+            "deliveryorderid": "หมายเลขใบเรียกเก็บเงิน",
+            "tradeid": "ข้อตกลง"
         },
         "spot": {
             "title": "รายการสินค้าในสต็อก",
@@ -1546,6 +1547,7 @@
             "bottom_pricing_detail": "รายละเอียด",
             "bottom_pricing_order": "มอบหมาย",
             "bottom_pricing_trade": "ตกลง",
+            "bottom_pricing_delivery": "กำลังตัด",
             "bottom_swap": "ตลาดแลกเปลี่ยน",
             "bottom_swap_position": "สรุปรวมทั้งหมด",
             "bottom_swap_order": "มอบหมาย",

+ 3 - 1
public/locales/zh-CN.json

@@ -890,7 +890,8 @@
             "reqtime": "申请时间",
             "applydate": "申请日期",
             "orderstatusdisplay": "单据状态",
-            "deliveryorderid": "交收单号"
+            "deliveryorderid": "交收单号",
+            "tradeid": "成交单号"
         },
         "spot": {
             "title": "现货提货单",
@@ -1546,6 +1547,7 @@
             "bottom_pricing_detail": "持仓明细",
             "bottom_pricing_order": "委托",
             "bottom_pricing_trade": "成交",
+            "bottom_pricing_delivery": "交收",
             "bottom_swap": "掉期市场",
             "bottom_swap_position": "持仓汇总",
             "bottom_swap_order": "委托",

+ 3 - 1
public/locales/zh-TW.json

@@ -890,7 +890,8 @@
             "reqtime": "申請時間",
             "applydate": "申請日期",
             "orderstatusdisplay": "單據狀態",
-            "deliveryorderid": "交收單號"
+            "deliveryorderid": "交收單號",
+            "tradeid": "成交單號"
         },
         "spot": {
             "title": "現貨提貨單",
@@ -1546,6 +1547,7 @@
             "bottom_pricing_detail": "持倉明細",
             "bottom_pricing_order": "委託",
             "bottom_pricing_trade": "成交",
+            "bottom_pricing_delivery": "交收",
             "bottom_swap": "掉期市場",
             "bottom_swap_position": "持倉彙總",
             "bottom_swap_order": "委託",

+ 8 - 4
public/proto/mtp.js

@@ -8130,21 +8130,25 @@ var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $pr
         type: "uint32",
         id: 5
       },
+      TradeID: {
+        type: "uint64",
+        id: 6
+      },
       BuyOrSell: {
         type: "uint32",
-        id: 6
+        id: 7
       },
       DeliveryLot: {
         type: "uint32",
-        id: 7
+        id: 8
       },
       DeliveryInfo: {
         type: "string",
-        id: 8
+        id: 9
       },
       ClientTicket: {
         type: "string",
-        id: 9
+        id: 10
       }
     }
   },

+ 5 - 4
public/proto/mtp.proto

@@ -2455,10 +2455,11 @@ message OfflineDeliveryReq {
 			optional uint32 UserID = 3; // �û�ID
 			optional string GoodsCode = 4; // ��Ʒ����
 		optional uint32 GoodsID = 5; // ��ƷID
-			optional uint32 BuyOrSell = 6; // ����
-			optional uint32 DeliveryLot = 7; // ��������
-			optional string DeliveryInfo = 8; // ������Ϣ
-			optional string ClientTicket = 9; // �ͻ�����ˮ��
+		optional uint64 TradeID = 6; // ��Ʒ����
+			optional uint32 BuyOrSell = 7; // ����
+			optional uint32 DeliveryLot = 8; // ��������
+			optional string DeliveryInfo = 9; // ������Ϣ
+			optional string ClientTicket = 10; // �ͻ�����ˮ��
 }
 // ���½�����������
 message OfflineDeliveryRsp {

+ 1 - 0
src/packages/pc/views/footer/goods/delivery/index.vue

@@ -55,6 +55,7 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { field: 'deliveryamount', label: 'delivery.offline.deliveryamount' },
     { field: 'deliveryinfo', label: 'delivery.offline.deliveryinfo' },
     { field: 'orderstatusdisplay', label: 'delivery.offline.orderstatusdisplay' },
+    { field: 'tradeid', label: 'delivery.offline.tradeid' },
     { field: 'deliveryorderid', label: 'delivery.offline.deliveryorderid' },
     { field: 'matchusername', label: 'delivery.offline.matchusername' },
     { field: 'reqtime', label: 'delivery.offline.reqtime' }

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

@@ -1,4 +1,4 @@
-<!-- 挂牌点价-持仓明细 -->
+<!-- 挂牌点价-持仓明细 - 按商品风控 -->
 <template>
     <app-table :data="tableList" v-model:columns="tableColumns">
         <!-- 商品代码/名称 -->
@@ -46,7 +46,8 @@ const { t } = i18n.global
 
 const { run } = useRequest(queryTradeHolderDetail, {
     params: {
-        trademodes: ' 10'
+        trademodes: ' 10',
+        riskcontrolmode: 1
     },
     onSuccess: (res) => initTableData(res.data)
 })

+ 227 - 0
src/packages/pc/views/footer/pricing/detail2/components/transfer/index.vue

@@ -0,0 +1,227 @@
+<!-- 挂牌点价-持仓明细-平仓 -->
+<template>
+    <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>
+            </el-form-item>
+            <el-form-item :label="t('position.transfer.buyorsell')">
+                <span>{{ getBuyOrSellName(selectedRow.buyorsell) }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.transfer.buycurholderamount')">
+                <span>{{ formatDecimal(selectedRow.holderamount) }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.transfer.holderqty')">
+                <span>{{ selectedRow.holderqty }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.transfer.holderprice')">
+                <span>{{ formatDecimal(selectedRow.holderprice, selectedRow.decimalplace) }}</span>
+            </el-form-item>
+            <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 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')" 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>
+            <el-form-item prop="OrderQty" :label="t('position.transfer.transferqty')">
+                <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>
+        <template #footer>
+            <el-button type="info" @click="onCancel(false)">{{ t('operation.cancel') }}</el-button>
+            <el-button type="primary" @click="onCloseSumit">{{ t('operation.submit') }}</el-button>
+        </template>
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType, computed, onMounted } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { useOrder } from '@/business/trade'
+import { formatDecimal, handlePriceColor, handleRequestBigNumber, parsePercent } from '@/filters'
+import { getBuyOrSellName, BuyOrSell, getPricemode2List, PriceMode } from '@/constants/order'
+import { useFuturesStore, usePositionStore, i18n, useSettingStore, useUserStore } from '@/stores'
+import { EBuildType, EDelistingType, EListingSelectType, EValidType, EOrderOperateType } from '@/constants/client'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+
+const props = defineProps({
+    selectedRow: {
+        type: Object as PropType<Model.TradeHolderDetailRsp>,
+        required: true
+    }
+})
+
+const futuresStore = useFuturesStore()
+const positionStore = usePositionStore()
+const userStore = useUserStore()
+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(() => {
+    const record = positionStore.positionList.find((e) => e.goodsid === props.selectedRow.goodsid && e.buyorsell === props.selectedRow.buyorsell)
+    const qty = props.selectedRow.holderqty - props.selectedRow.freezeqty
+    return Math.min(record?.enableqty ?? 0, qty)
+})
+
+// 损益
+const closepl = computed(() => {
+    const { presettle = 0, last = 0 } = quote.value ?? {}
+    const { holderqty, holderamount, agreeunit, buyorsell, currencyid, tacurrencyid } = props.selectedRow
+    const price = last || presettle // 没有最新价取昨结价
+    
+    // 查找汇率
+    let exchangerate = 1
+    if (currencyid !== tacurrencyid) {
+        const currency = userStore.userData.exchangeRateConfigs.find((e) => e.descurrencyid === tacurrencyid && e.oricurrencyid === currencyid)
+        exchangerate = currency?.exchangerate ?? 0
+    }
+    
+    // 计算市值 = 现价 * 数量 * 合约单位
+    const marketValue = price ? price * holderqty * agreeunit * exchangerate : 0
+    return price ? (marketValue - holderamount) * (buyorsell === BuyOrSell.Buy ? 1 : -1) : 0
+})
+
+const formRules: FormRules = {
+    OrderPrice: [{
+        message: t('position.transfer.tips3'),
+        validator: () => {
+            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: () => {
+            return !!formData.OrderQty
+        }
+    }],
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = 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) {
+            const { marketid, goodsid, buyorsell, tradeid } = props.selectedRow
+            /// 市场ID
+            formData.Header = { MarketID: marketid, GoodsID: goodsid }
+            formData.MarketID = marketid
+            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 = EOrderOperateType.ORDEROPERATETYPE_HOLDER_CLOSE
+            formData.RelatedID = handleRequestBigNumber(tradeid)
+
+            formSubmit().then(() => {
+                if (settingStore.getSettingValue('showOrderSuccessMessage')) {
+                    ElMessage.success(t('position.transfer.tips2'))
+                }
+                onCancel(true)
+            }).catch((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) {
+        case BuyOrSell.Buy:
+            formData.OrderPrice = ask || presettle
+            break
+        case BuyOrSell.Sell:
+            formData.OrderPrice = bid || presettle
+            break
+        default:
+            formData.OrderPrice = presettle
+    }
+    formData.OrderPrice = getOrderPrice()
+    formData.OrderQty = maxQty.value
+    formData.PriceMode = PriceMode.Market
+    formData.MarketMaxSub = 100.0
+})
+</script>

+ 68 - 0
src/packages/pc/views/footer/pricing/detail2/index.vue

@@ -0,0 +1,68 @@
+<!-- 挂牌点价-持仓明细 按账户风控 -->
+<template>
+    <app-table :data="tableList" v-model:columns="tableColumns">
+        <!-- 商品代码/名称 -->
+        <template #goodsname="{ row }">
+            {{ row.goodscode }}/{{ row.goodsname }}
+        </template>
+        <!-- 方向 -->
+        <template #buyorsell="{ value }">
+            {{ getBuyOrSellName(value) }}
+        </template>
+        <!-- 操作 -->
+        <template #operate="{ row }">
+            <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>
+        </template>
+        <template #footer>
+            <component ref="componentRef" v-bind="{ selectedRow }" :is="componentMap.get(componentId)"
+                @closed="closeComponent" v-if="componentId" />
+        </template>
+    </app-table>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, defineAsyncComponent, onUnmounted } from 'vue'
+import { getBuyOrSellName } from '@/constants/order'
+import { useRequest } from '@/hooks/request'
+import { useComponent } from '@/hooks/component'
+import { useLocalPagination } from '@/hooks/pagination'
+import { querySBYJMyOrders } from '@/services/api/order'
+import { useTableColumnsStore, i18n } from '@/stores'
+import AppTable from '@pc/components/base/table/index.vue'
+import eventBus from '@/services/bus'
+
+const componentMap = new Map<string, unknown>([
+    ['transfer', defineAsyncComponent(() => import('./components/transfer/index.vue'))],
+])
+
+const { getTableColumns } = useTableColumnsStore()
+const { tableList, showLoadMore, initTableData, loadMore } = useLocalPagination<Model.SBYJMyOrderRsp>()
+const selectedRow = shallowRef<Model.SBYJMyOrderRsp>()
+const tableColumns = shallowRef<Model.TableColumn[]>([])
+const { t } = i18n.global
+
+const { run } = useRequest(querySBYJMyOrders, {
+    onSuccess: (res) => initTableData(res.data)
+})
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
+    run()
+})
+
+// 接收头寸变化通知通知
+const posChangedNtf = eventBus.$on('PosChangedNtf', () => run())
+
+const showComponent = (componentName: string, row: Model.SBYJMyOrderRsp) => {
+    selectedRow.value = row
+    openComponent(componentName)
+}
+
+getTableColumns('order-detail2', true).then((res) => {
+    tableColumns.value = res
+})
+
+onUnmounted(() => posChangedNtf.cancel())
+</script>

+ 144 - 0
src/packages/pc/views/footer/pricing/position/components/delivery/index.vue

@@ -0,0 +1,144 @@
+<!-- 挂牌点价-持仓汇总-交收 -->
+<template>
+    <app-drawer :title="t('operation.delivery')" :width="840" v-model:show="show" :loading="loading" :refresh="refresh">
+        <el-form ref="formRef" class="el-form--horizontal" label-width="140px" :model="formData" :rules="formRules">
+            <el-form-item :label="t('position.goods.goodsname')">
+                <span>{{ position.goodscode }}/{{ position.goodsname }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.buyorsell')">
+                <span>{{ getBuyOrSellName(position.buyorsell) }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.curholderamount')">
+                <span>{{ formatDecimal(position.curholderamount) }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.curpositionqty')">
+                <span>{{ position.curpositionqty }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.frozenqty')">
+                <span>{{ position.frozenqty }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.enableqty')">
+                <span>{{ position.enableqty }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.mindeliverylot')">
+                <span>{{ position.mindeliverylot }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.averageprice')">
+                <span>{{ formatDecimal(position.averageprice, position.decimalplace) }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.matchname')">
+                <span>{{ position.matchname }}</span>
+            </el-form-item>
+            <el-form-item :label="t('position.goods.closepl')">
+                <span :class="position.closeplColor">
+                    {{ formatDecimal(position.closepl, position.decimalplace) }}
+                </span>
+            </el-form-item>
+            <el-form-item prop="DeliveryLot" :label="t('position.goods.deliverylot')">
+                <el-input-number :placeholder="t('common.pleaseenter')" v-model="formData.DeliveryLot" :precision="0"
+                    :max="position.enableqty" :min="0" />
+            </el-form-item>
+            <el-form-item :label="t('position.goods.deliveryqty')">
+                <span>{{ (formData.DeliveryLot ?? 0) * position.agreeunit }}{{ position.goodunit }}</span>
+            </el-form-item>
+            <el-form-item class="el-form-item--row" prop="DeliveryInfo"
+                :label="position.buyorsell === BuyOrSell.Buy ? t('position.goods.address') : t('position.goods.deliveryinfo')">
+                <div class="el-form-item--col">
+                    <el-input type="textarea" :placeholder="position.buyorsell === BuyOrSell.Buy ? t('position.goods.tips9') : t('position.goods.tips10')"
+                        :rows="3" v-model="formData.DeliveryInfo" />
+                    <el-icon :size="20" style="cursor: pointer;" @click="showAddress = true"
+                        v-if="position.buyorsell === BuyOrSell.Buy">
+                        <CirclePlusFilled />
+                    </el-icon>
+                </div>
+            </el-form-item>
+        </el-form> 
+        <app-address v-model:show="showAddress" @change="onAddressChange" />
+        <template #footer>
+            <el-button type="info" @click="onCancel(false)">{{ t('operation.cancel') }}</el-button>
+            <el-button type="primary" @click="onSubmit">{{ t('operation.delivery') }}</el-button>
+        </template> 
+    </app-drawer>
+</template>
+
+<script lang="ts" setup>
+import { ref, PropType } from 'vue'
+import { ElMessage, FormInstance, FormRules } from 'element-plus'
+import { useOfflineDelivery } from '@/business/trade'
+import { getBuyOrSellName, BuyOrSell } from '@/constants/order'
+import { formatDecimal } from '@/filters'
+import { i18n } from '@/stores'
+import AppDrawer from '@pc/components/base/drawer/index.vue'
+import AppAddress from '@pc/components/modules/address/index.vue'
+
+const { t } = i18n.global
+
+const props = defineProps({
+    position: {
+        type: Object as PropType<Model.TradePositionRsp & {
+            closepl: number; // 浮动盈亏
+            closeplColor: string; // 浮动盈亏颜色
+        }>,
+        required: true,
+    }
+})
+
+const { formSubmit, formData, loading } = useOfflineDelivery()
+const show = ref(true)
+const refresh = ref(false)
+const showAddress = ref(false)
+const formRef = ref<FormInstance>()
+
+// 表单验证规则
+const formRules: FormRules = {
+    DeliveryLot: [{
+        required: true,
+        message: t('position.goods.tips7'),
+        validator: (rule, value, callback) => {
+            if (value >= props.position.mindeliverylot) {
+                callback()
+            } else {
+                callback(new Error(t('position.goods.tips8') + `${props.position.mindeliverylot}`))
+            }
+        }
+    }],
+    DeliveryInfo: [{
+        required: true,
+        message: props.position.buyorsell === BuyOrSell.Buy ? t('position.goods.tips9') : t('position.goods.tips10'),
+        validator: () => {
+            return !!formData.DeliveryInfo
+        }
+    }],
+}
+
+// 选择地址
+const onAddressChange = (item: Model.UserReceiveInfoRsp) => {
+    const contact = `${item.receivername} ${item.phonenum}`
+    const address = `${item.provincename} ${item.cityname} ${item.districtname} ${item.address}`
+    formData.DeliveryInfo = [contact, address].join('\n')
+}
+
+const onCancel = (isRefresh = false) => {
+    show.value = false
+    refresh.value = isRefresh
+}
+
+const onSubmit = () => {
+    formRef.value?.validate((valid) => {
+        if (valid) {
+            const { marketid, goodsid, goodscode, buyorsell } = props.position
+            /// 市场ID
+            formData.Header = { MarketID: marketid, GoodsID: goodsid }
+            formData.GoodsCode = goodscode
+            formData.GoodsID = goodsid
+            formData.BuyOrSell = buyorsell
+            formSubmit().then(() => {
+                ElMessage.success(t('common.submitsuccess'))
+                onCancel(true)
+            }).catch((err) => {
+                ElMessage.error(t('common.submitfailure') + err)
+            })
+        }
+    })
+}
+</script>

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

@@ -26,7 +26,8 @@
         </template>
         <!-- 操作 -->
         <template #operate="{ row }">
-            <el-button type="danger" size="small" @click="showComponent('transfer', row)">{{ t('operation.close') }}</el-button>
+            <el-button v-if="row.riskcontrolmode != 1" type="danger" size="small" @click="showComponent('transfer', row)">{{ t('operation.close') }}</el-button>
+            <el-button v-if="showDelivery(row)" type="primary" size="small" @click="showComponent('delivery', row)">{{ t('operation.delivery') }}</el-button>
         </template>
         <template #footer>
             <component ref="componentRef" v-bind="{ position: selectedRow }" :is="componentMap.get(componentId)"
@@ -41,15 +42,17 @@ import { formatDecimal, handleNumberValue } from '@/filters'
 import { getBuyOrSellName } from '@/constants/order'
 import { useComponent } from '@/hooks/component'
 import { useComposeTable } from '@pc/components/base/table'
-import { usePositionStore, i18n } from '@/stores'
+import { usePositionStore, i18n, useFuturesStore } from '@/stores'
 import AppTable from '@pc/components/base/table/index.vue'
 
 const componentMap = new Map<string, unknown>([
     ['transfer', defineAsyncComponent(() => import('./components/transfer/index.vue'))],
+    ['delivery', defineAsyncComponent(() => import('./components/delivery/index.vue'))],
 ])
 
 const { rowKey, expandKeys, rowClick } = useComposeTable<Model.TradePositionRsp>({ rowKey: 'pkid' })
 const { componentRef, componentId, openComponent, closeComponent } = useComponent()
+const futuresStore = useFuturesStore()
 const positionStore = usePositionStore()
 const selectedRow = shallowRef<Model.TradePositionRsp>()
 
@@ -57,6 +60,12 @@ const positionList = computed(() => positionStore.getPositionListByTradeMode(10)
 
 const { t } = i18n.global
 
+const showDelivery = (row: Model.TradePositionRsp) => {
+    // 是否供应商
+    const goods = futuresStore.getGoods(row.goodsid)
+    return goods?.deliveryflag === 1 && row.riskcontrolmode != 1
+}
+
 const tableColumns = shallowRef<Model.TableColumn[]>([
     { field: 'goodsname', label: 'position.pricing.goodsname' },
     { field: 'buyorsell', label: 'position.pricing.buyorsell' },
@@ -67,7 +76,7 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
     { field: 'frozenqty', label: 'position.pricing.frozenqty' },
     { field: 'enableqty', label: 'position.pricing.enableqty' },
     { field: 'closepl', label: 'position.pricing.closepl' },
-    { field: 'operate', label: 'common.operate', fixed: 'right' },
+    { field: 'operate', label: 'common.operate', fixed: 'right', width: 140 },
 ])
 
 const showComponent = (componentName: string, row: Model.TradePositionRsp) => {

+ 1 - 0
src/types/model/account.d.ts

@@ -185,6 +185,7 @@ declare global {
             reckonpricealgorithm: number; // 结算价算法: 1:最后多少笔成交价加权平均 2:最后多少秒成交价加权平均 3:全天加权平均 4:最后一口价 5.买一价 6.卖一价 7.买一卖一均价 8.外部结算价
             reckonpriceparam: number; // 结算价参数
             reckontime: string; // 结算时间通道交易-对冲
+            riskcontrolmode: number; // 风控方式(52模式) 1:按单风控 2:按账户风控
             roleprioritytype: number; // 角色优先类型 - 1:无 2:报价商优先 3:非报价商优先 [16:挂牌点选]
             selllistingauditflag: number; // 卖挂牌是否需要审核(仓单贸易) - 0:不需要 1:需要
             trademarkettype: number; // 交易市场类型 - 1:合约市场 2:外部市场 3:仓单市场

+ 6 - 0
src/types/model/order.d.ts

@@ -713,6 +713,8 @@ declare namespace Model {
         goodsid?: number
         /// 买卖方向 0-买 1-卖
         buyorsell?: number
+        /// 风控方式(52模式) 1:按单风控 2:按账户风控
+        riskcontrolmode?: number
     }
 
     /*  查询我的持仓/掉期持仓-持仓明细 响应*/
@@ -785,6 +787,8 @@ declare namespace Model {
         releaseamount: number
         /// 释放持仓授信金额
         releaseholdercredit: number
+        /// 风控方式(52模式) 1:按单风控 2:按账户风控
+        riskcontrolmode: number
         /// 资金账号名称
         taname: string
         /// 成交金额
@@ -999,6 +1003,8 @@ declare namespace Model {
         refgoodscode: string
         /// 参考商品ID
         refgoodsid: number
+        /// 风控方式(52模式) 1:按单风控 2:按账户风控
+        riskcontrolmode: number
         /// T+N冻结总量
         tnqty: number
         /// T+N使用量(可以使用T+N的冻结数量)

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

@@ -328,6 +328,7 @@ declare namespace Model {
         orderstatusdisplay: string; // 单据状态
         reqtime: string; // 申请时间
         reqtradedate: string; // 申请交易日
+        tradeid: string; // 成交单号(10-线下交收)
         userid: number; // 申请用户ID
     }
 

+ 1 - 0
src/types/proto/trade.d.ts

@@ -609,6 +609,7 @@ declare global {
             UserID?: number; // 用户ID
             GoodsCode?: string; // 商品代码
             GoodsID?: number; // 商品ID
+            TradeID?: string | Long; // uint64 交易ID
             BuyOrSell?: number; // 方向
             DeliveryLot?: number; // 交收手数
             DeliveryInfo?: string; // 交收信息