|
@@ -0,0 +1,367 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="pricing-listing">
|
|
|
|
|
+ <div class="pricing-listing__forex" v-if="selectedGoods">
|
|
|
|
|
+ <Forex v-bind="{ goodsCode: selectedGoods.goodscode }" @price-click="onPriceClick" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="pricing-listing__form">
|
|
|
|
|
+ <h4 class="header-title">挂牌点价</h4>
|
|
|
|
|
+ <el-form ref="formRef" class="el-form--vertical" label-width="60px" :show-message="false" :model="formData"
|
|
|
|
|
+ :rules="formRules">
|
|
|
|
|
+ <el-form-item prop="GoodsID" label="商品">
|
|
|
|
|
+ <el-select effect="dark" placeholder="请选择" v-model="selectedGoodsId" filterable>
|
|
|
|
|
+ <el-option :label="item.goodsname" :value="item.goodsid" v-for="(item, index) in marketGoodsList"
|
|
|
|
|
+ :key="index" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item prop="BuyOrSell" label="方向">
|
|
|
|
|
+ <el-radio-group v-model="formData.BuyOrSell">
|
|
|
|
|
+ <el-radio v-for="(item, index) in getBuyOrSellList()" :key="index" :label="item.value">
|
|
|
|
|
+ {{ item.label }}
|
|
|
|
|
+ </el-radio>
|
|
|
|
|
+ </el-radio-group>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item prop="PriceMode" label="方式">
|
|
|
|
|
+ <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="OrderPrice" label="价格" v-if="formData.PriceMode === PriceMode.Market">
|
|
|
|
|
+ <div class="el-form-item--col">
|
|
|
|
|
+ <span>{{ marketPrice.toFixed(decimalplace) }}</span>
|
|
|
|
|
+ <div v-if="selectedGoods">
|
|
|
|
|
+ <div class="row-price g-price-up">
|
|
|
|
|
+ <Icon icon="Top" />
|
|
|
|
|
+ <span>{{ selectedGoods.limitup.toFixed(decimalplace) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="row-price g-price-down">
|
|
|
|
|
+ <Icon icon="Bottom" />
|
|
|
|
|
+ <span>{{ selectedGoods.limitdown.toFixed(decimalplace) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item prop="MarketMaxSub" label="点差" v-if="formData.PriceMode === PriceMode.Market">
|
|
|
|
|
+ <el-input-number ref="priceRef" placeholder="请输入" :min="0" :max="9999999999" v-model="formData.MarketMaxSub"
|
|
|
|
|
+ @keyup.enter="submitFocus" integer />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item prop="OrderPrice" label="价格" v-if="formData.PriceMode === PriceMode.Limit">
|
|
|
|
|
+ <el-input-number ref="priceRef" placeholder="请输入" :max="9999999999" :min="0" v-model="formData.OrderPrice"
|
|
|
|
|
+ :auto-fixed="false" :decimal-length="decimalplace" :step="decimalvalue" @keyup.enter="submitFocus" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item prop="OrderQty" label="数量">
|
|
|
|
|
+ <div class="g-qty-group">
|
|
|
|
|
+ <el-input-number ref="qtyRef" placeholder="请输入" :min="0" :max="9999999999" :precision="0" :step="qtyStep || 1"
|
|
|
|
|
+ v-model="formData.OrderQty" @keyup.enter="submitFocus" />
|
|
|
|
|
+ <el-radio-group size="small" v-model="qtyStep" :validate-event="false" @change="onRadioChange">
|
|
|
|
|
+ <el-radio v-for="(value, index) in qtyStepList" :key="index" :label="value" border />
|
|
|
|
|
+ </el-radio-group>
|
|
|
|
|
+ <template
|
|
|
|
|
+ v-if="(formData.BuyOrSell === BuyOrSell.Buy || selectedGoods?.tradeproperty !== 2) && settingStore.getSettingValue('showOrderEnableQty')">
|
|
|
|
|
+ <div
|
|
|
|
|
+ style="display: flex;flex-direction: column;line-height: normal;font-size: 12px;color: #7a8a94;">
|
|
|
|
|
+ <span>预估可订立量:{{ total.enableQty }}</span>
|
|
|
|
|
+ <span>预扣保证金:{{ total.deposit.toFixed(2) }}</span>
|
|
|
|
|
+ <span>可用资金:{{ accountStore.currentAccount.avaiableMoney?.toFixed(2) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item class="footer-btnbar">
|
|
|
|
|
+ <template v-if="formData.BuyOrSell === BuyOrSell.Buy">
|
|
|
|
|
+ <el-button ref="submitRef" type="danger" :disabled="!selectedGoodsId || !formData.OrderQty"
|
|
|
|
|
+ @click="onBeforeSubmit(BuildType.Open)"
|
|
|
|
|
+ v-if="!selectedGoods?.iscannotbuy">订立</el-button>
|
|
|
|
|
+ <el-button type="primary"
|
|
|
|
|
+ :disabled="!formData.OrderQty || !sellQty || (formData.OrderQty > sellQty)"
|
|
|
|
|
+ @click="onBeforeSubmit(BuildType.Close)" v-if="!isTrademode16">
|
|
|
|
|
+ <span>转让</span>
|
|
|
|
|
+ <span v-if="sellQty">(≤{{ sellQty }})</span>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template v-if="formData.BuyOrSell === BuyOrSell.Sell">
|
|
|
|
|
+ <el-button ref="submitRef" type="success" :disabled="!selectedGoodsId || !formData.OrderQty"
|
|
|
|
|
+ @click="onBeforeSubmit(BuildType.Open)"
|
|
|
|
|
+ v-if="!isTrademode16 && !selectedGoods?.iscannotsell">订立</el-button>
|
|
|
|
|
+ <el-button type="primary" :disabled="!formData.OrderQty || !buyQty || (formData.OrderQty > buyQty)"
|
|
|
|
|
+ @click="onBeforeSubmit(BuildType.Close)">
|
|
|
|
|
+ <span>转让</span>
|
|
|
|
|
+ <span v-if="buyQty">(≤{{ buyQty }})</span>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+import { shallowRef, computed, watch, onMounted } from 'vue'
|
|
|
|
|
+import { ElMessage, ElMessageBox, FormInstance, FormRules } from 'element-plus'
|
|
|
|
|
+import { BuyOrSell, getBuyOrSellList, getPricemode2List, PriceMode, BuildType } from '@/constants/order'
|
|
|
|
|
+import { useOrder } from '@/business/trade'
|
|
|
|
|
+import { useFuturesStore, useSettingStore, useAccountStore, usePositionStore } from '@/stores'
|
|
|
|
|
+import Forex from '@pc/components/modules/quote/forex/index.vue'
|
|
|
|
|
+import Icon from '@pc/components/base/icon/index.vue'
|
|
|
|
|
+
|
|
|
|
|
+const accountStore = useAccountStore()
|
|
|
|
|
+const settingStore = useSettingStore()
|
|
|
|
|
+const futuresStore = useFuturesStore()
|
|
|
|
|
+
|
|
|
|
|
+const { selectedGoodsId, selectedGoods, marketGoodsList } = futuresStore.$toRefs()
|
|
|
|
|
+const { formData, formSubmit, loading } = useOrder()
|
|
|
|
|
+const positionStore = usePositionStore()
|
|
|
|
|
+const formRef = shallowRef<FormInstance>()
|
|
|
|
|
+const qtyStepList = [1, 10, 100] // 数量步长列表
|
|
|
|
|
+const qtyStep = shallowRef(qtyStepList[0]) // 数量步长
|
|
|
|
|
+
|
|
|
|
|
+const priceRef = shallowRef()
|
|
|
|
|
+const qtyRef = shallowRef()
|
|
|
|
|
+const submitRef = shallowRef()
|
|
|
|
|
+
|
|
|
|
|
+const marketPrice = computed(() => {
|
|
|
|
|
+ const { ask = 0, bid = 0 } = selectedGoods.value ?? {}
|
|
|
|
|
+ return formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const isTrademode16 = computed(() => selectedGoods.value?.trademode === 16)
|
|
|
|
|
+
|
|
|
|
|
+const total = computed(() => {
|
|
|
|
|
+ const { avaiableMoney = 0 } = accountStore.currentAccount
|
|
|
|
|
+ const { marketmarginalgorithm = 0, marketmarginvalue = 0, agreeunit = 0 } = selectedGoods.value ?? {}
|
|
|
|
|
+
|
|
|
|
|
+ const result = {
|
|
|
|
|
+ enableQty: 0,
|
|
|
|
|
+ deposit: 0
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const fixed = agreeunit * marketmarginvalue
|
|
|
|
|
+ const ratio = fixed * (formData.OrderPrice ?? 0)
|
|
|
|
|
+
|
|
|
|
|
+ if (fixed && ratio) {
|
|
|
|
|
+ if (marketmarginalgorithm === 1) {
|
|
|
|
|
+ const qty = Math.trunc(avaiableMoney / ratio)
|
|
|
|
|
+ result.deposit = (formData.OrderQty ?? 0) * ratio
|
|
|
|
|
+ result.enableQty = qty > 0 ? qty : 0
|
|
|
|
|
+ }
|
|
|
|
|
+ if (marketmarginalgorithm === 2) {
|
|
|
|
|
+ const qty = Math.trunc(avaiableMoney / fixed)
|
|
|
|
|
+ result.deposit = (formData.OrderQty ?? 0) * fixed
|
|
|
|
|
+ result.enableQty = qty > 0 ? qty : 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const position = shallowRef<Model.TradePositionRsp[]>([]) // 持仓汇总
|
|
|
|
|
+
|
|
|
|
|
+// 买方向持仓数量
|
|
|
|
|
+const buyQty = computed(() => positionStore.getOrderQty(BuyOrSell.Buy, selectedGoodsId.value))
|
|
|
|
|
+
|
|
|
|
|
+// 卖方向持仓数量
|
|
|
|
|
+const sellQty = computed(() => positionStore.getOrderQty(BuyOrSell.Sell, selectedGoodsId.value))
|
|
|
|
|
+
|
|
|
|
|
+// 价格类型
|
|
|
|
|
+const orderPriceType = computed(() => settingStore.getSettingValue('orderPriceType'))
|
|
|
|
|
+
|
|
|
|
|
+// 买卖方向
|
|
|
|
|
+const orderBuyOrSell = computed(() => settingStore.getSettingValue('orderBuyOrSell'))
|
|
|
|
|
+
|
|
|
|
|
+// 是否禁用价格输入
|
|
|
|
|
+const isDisabled = computed(() => [3, 4].includes(orderPriceType.value))
|
|
|
|
|
+
|
|
|
|
|
+const { decimalplace = 0.0, decimalvalue = 0.0 } = selectedGoods.value ?? { }
|
|
|
|
|
+
|
|
|
|
|
+const formRules: FormRules = {
|
|
|
|
|
+ OrderPrice: [{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
|
|
+ if (value) {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback(new Error('请输入价格'))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }],
|
|
|
|
|
+ MarketMaxSub: [{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ 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('请输入数量'))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }],
|
|
|
|
|
+ SlPrice: [{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
|
|
+ if (value) {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback(new Error('请输入止损价'))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }],
|
|
|
|
|
+ SpPrice: [{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ type: 'number',
|
|
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
|
|
+ if (value) {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback(new Error('请输入止盈价'))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }],
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const onPriceClick = (buyorsell: BuyOrSell, value: number) => {
|
|
|
|
|
+ formData.BuyOrSell = buyorsell === BuyOrSell.Sell ? BuyOrSell.Buy : BuyOrSell.Sell
|
|
|
|
|
+ if (isDisabled.value) {
|
|
|
|
|
+ qtyInputFocus()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ formData.OrderPrice = value
|
|
|
|
|
+ priceInputFocus()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 价格输入框获取焦点
|
|
|
|
|
+const priceInputFocus = () => {
|
|
|
|
|
+ priceRef.value.focus()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 数量输入框获取焦点
|
|
|
|
|
+const qtyInputFocus = () => {
|
|
|
|
|
+ qtyRef.value.focus()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 提交按钮获取焦点
|
|
|
|
|
+const submitFocus = () => {
|
|
|
|
|
+ submitRef.value.ref.focus()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const onRadioChange = (value: number) => {
|
|
|
|
|
+ formData.OrderQty = value
|
|
|
|
|
+ qtyInputFocus()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const onBeforeSubmit = (buildType: BuildType) => {
|
|
|
|
|
+ if (!loading.value) {
|
|
|
|
|
+ formRef.value?.validate((valid) => {
|
|
|
|
|
+ if (valid) {
|
|
|
|
|
+ formData.BuildType = buildType
|
|
|
|
|
+ if (buildType === BuildType.Close) {
|
|
|
|
|
+ const { ask = 0, bid = 0 } = selectedGoods.value ?? {}
|
|
|
|
|
+ formData.OrderPrice = formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
|
|
|
|
|
+
|
|
|
|
|
+ const datas = position.value.filter(item => formData.BuyOrSell === BuyOrSell.Buy ? item.buyorsell === BuyOrSell.Sell : item.buyorsell === BuyOrSell.Buy) // 反方向持仓
|
|
|
|
|
+ if (datas.length > 0) {
|
|
|
|
|
+ formData.OrderQty = datas[0].enableqty
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (settingStore.getSettingValue('showOrderDialog')) {
|
|
|
|
|
+ ElMessageBox.confirm(
|
|
|
|
|
+ '是否立即挂牌?',
|
|
|
|
|
+ '提示'
|
|
|
|
|
+ ).then(() => onSubmit())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ onSubmit()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 提交挂牌
|
|
|
|
|
+const onSubmit = () => {
|
|
|
|
|
+ const { marketid = 0, goodsid = 0 } = selectedGoods.value ?? {}
|
|
|
|
|
+ /// 获取对应的市场ID
|
|
|
|
|
+ formData.MarketID = marketid
|
|
|
|
|
+ formData.GoodsID = goodsid
|
|
|
|
|
+ if (formData.PriceMode === PriceMode.Market) { formData.OrderPrice = marketPrice.value }
|
|
|
|
|
+
|
|
|
|
|
+ formSubmit().then(() => {
|
|
|
|
|
+ if (settingStore.getSettingValue('orderQtyIsEmpty')) {
|
|
|
|
|
+ formData.OrderQty = undefined
|
|
|
|
|
+ qtyStep.value = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ if (settingStore.getSettingValue('showOrderSuccessMessage')) {
|
|
|
|
|
+ ElMessage.success('挂牌成功')
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch((err) => {
|
|
|
|
|
+ if (settingStore.getSettingValue('showOrderFailMessage')) {
|
|
|
|
|
+ ElMessage.error('挂牌失败:' + err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }).finally(() => {
|
|
|
|
|
+ const focusType = settingStore.getSettingValue('orderFocusType')
|
|
|
|
|
+ if (focusType === 2 || isDisabled.value) {
|
|
|
|
|
+ qtyInputFocus()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ priceInputFocus()
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getOrderPrice = () => {
|
|
|
|
|
+ const { last, bid, ask, presettle = 0 } = selectedGoods.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
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+watch([selectedGoodsId, orderPriceType], () => {
|
|
|
|
|
+ formData.OrderPrice = getOrderPrice()
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+watch(orderBuyOrSell, (val) => {
|
|
|
|
|
+ formData.BuyOrSell = val
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+watch([() => formData.BuyOrSell, () => selectedGoods.value?.last, () => selectedGoods.value?.bid, () => selectedGoods.value?.ask], () => {
|
|
|
|
|
+ if (isDisabled.value) {
|
|
|
|
|
+ formData.OrderPrice = getOrderPrice()
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ formData.OrderPrice = getOrderPrice()
|
|
|
|
|
+ formData.OrderQty = qtyStep.value
|
|
|
|
|
+ formData.BuyOrSell = orderBuyOrSell.value
|
|
|
|
|
+ formData.PriceMode = PriceMode.Market
|
|
|
|
|
+ formData.BuildType = BuildType.Open
|
|
|
|
|
+ formData.MarketMaxSub = 100.0
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="less">
|
|
|
|
|
+@import './index.less';
|
|
|
|
|
+</style>
|