|
|
@@ -0,0 +1,346 @@
|
|
|
+<template>
|
|
|
+ <app-view class="pricing-trade">
|
|
|
+ <template #header>
|
|
|
+ <app-navbar :title="quote ? `${quote.goodscode}/${quote.goodsname}` : $t('quote.pricing.title')" >
|
|
|
+ <template #right>
|
|
|
+ <Icon name="bars" size="20px" @click="openComponent('detail')" />
|
|
|
+ </template>
|
|
|
+ </app-navbar>
|
|
|
+ </template>
|
|
|
+ <div class="pricing-trade__form" v-if="quote">
|
|
|
+ <div class="form-price">
|
|
|
+ <dl>
|
|
|
+ <dt>买价</dt>
|
|
|
+ <dd :class="quote.bidColor">{{ handleNumberValue(formatDecimal(quote.ask, quote.decimalplace)) }}
|
|
|
+ </dd>
|
|
|
+ </dl>
|
|
|
+ <dl>
|
|
|
+ <dt>卖价</dt>
|
|
|
+ <dd :class="quote.askColor">{{ handleNumberValue(formatDecimal(quote.bid, quote.decimalplace)) }}
|
|
|
+ </dd>
|
|
|
+ </dl>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 下单 -->
|
|
|
+ <Form ref="formRef" class="g-form__container" @submit="onSubmit">
|
|
|
+ <CellGroup inset>
|
|
|
+ <Field :label="$t('quote.pricing.buyorsell')">
|
|
|
+ <template #input>
|
|
|
+ <RadioGroup v-model="formData.BuyOrSell" direction="horizontal" @click="onBuyOrSellChanged">
|
|
|
+ <Radio v-for="(item, index) in getBuyOrSellList()" :key="index" :name="item.value">{{ item.label
|
|
|
+ }}</Radio>
|
|
|
+ </RadioGroup>
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <Field :label="$t('quote.pricing.pricemode')">
|
|
|
+ <template #input>
|
|
|
+ <RadioGroup v-model="formData.PriceMode" direction="horizontal" @click="onPriceModeChanged">
|
|
|
+ <Radio v-for="(item, index) in getPricemode2List()" :key="index" :name="item.value">{{
|
|
|
+ item.label
|
|
|
+ }}</Radio>
|
|
|
+ </RadioGroup>
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <Field name="OrderQty" :label="$t('quote.pricing.orderqty')">
|
|
|
+ <template #input>
|
|
|
+ <div class="g-qty-group">
|
|
|
+ <div class="g-qty-group__stepper">
|
|
|
+ <Stepper v-model="formData.OrderQty" theme="round" button-size="22" :min="0"
|
|
|
+ :step="qtyStep" integer />
|
|
|
+ </div>
|
|
|
+ <RadioGroup v-model="qtyStep" direction="horizontal" @change="onRadioChange">
|
|
|
+ <Radio v-for="(value, index) in qtyStepList" :key="index" :name="value">{{ value }}
|
|
|
+ </Radio>
|
|
|
+ </RadioGroup>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <!-- 市价 -->
|
|
|
+ <!-- <Field label="价格" v-if="formData.PriceMode === PriceMode.Market">
|
|
|
+ <template #input>
|
|
|
+ <span>{{ handleNumberValue(marketPrice) }}</span>
|
|
|
+ </template>
|
|
|
+ </Field> -->
|
|
|
+ <!-- 允许成交范围 -->
|
|
|
+ <Field name="MarketMaxSub" :rules="formRules.MarketMaxSub" :label="$t('quote.pricing.marketmaxsub')" v-if="formData.PriceMode === PriceMode.Market">
|
|
|
+ <template #input>
|
|
|
+ <Stepper v-model="formData.MarketMaxSub" theme="round" button-size="22" :auto-fixed="false"
|
|
|
+ integer />
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <!-- 限价 -->
|
|
|
+ <Field name="OrderPrice" :rules="formRules.OrderPrice" :label="$t('quote.pricing.price')" v-if="formData.PriceMode === PriceMode.Limit">
|
|
|
+ <template #input>
|
|
|
+ <Stepper v-model="formData.OrderPrice" theme="round" button-size="22" :min="0"
|
|
|
+ :auto-fixed="false" :decimal-length="decimalplace" :step="decimalvalue" />
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <!-- <Field name="SlPrice" :rules="formRules.SlPrice"
|
|
|
+ v-if="formData.PriceMode === PriceMode.Limit && formData.BuildType === BuildType.Open">
|
|
|
+ <template #label>
|
|
|
+ <Checkbox v-model="sl">止损</Checkbox>
|
|
|
+ </template>
|
|
|
+ <template #input>
|
|
|
+ <Stepper v-model="formData.SlPrice" :disabled="!sl" theme="round" button-size="22" allow-empty
|
|
|
+ :default-value="0" :min="0" :decimal-length="decimalplace" :step="decimalvalue" />
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <Field name="SpPrice" :rules="formRules.SpPrice"
|
|
|
+ v-if="formData.PriceMode === PriceMode.Limit && formData.BuildType === BuildType.Open">
|
|
|
+ <template #label>
|
|
|
+ <Checkbox v-model="sp">止盈</Checkbox>
|
|
|
+ </template>
|
|
|
+ <template #input>
|
|
|
+ <Stepper v-model="formData.SpPrice" :disabled="!sp" theme="round" button-size="22" allow-empty
|
|
|
+ :default-value="0" :min="0" :decimal-length="decimalplace" :step="decimalvalue" />
|
|
|
+ </template>
|
|
|
+ </Field> -->
|
|
|
+ <!-- <template v-if="formData.BuyOrSell === BuyOrSell.Buy || quote?.tradeproperty !== 2">
|
|
|
+ <Field :label="$t('quote.pricing.enableQty')">
|
|
|
+ <template #input>
|
|
|
+ <span>{{ total.enableQty }}</span>
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <Field :label="$t('quote.pricing.deposit')">
|
|
|
+ <template #input>
|
|
|
+ <span>{{ total.deposit.toFixed(2) }}</span>
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ </template>
|
|
|
+ <Field :label="$t('quote.pricing.avaiableMoney')">
|
|
|
+ <template #input>
|
|
|
+ <span>{{ accountStore.currentAccount.avaiableMoney?.toFixed(2) }}</span>
|
|
|
+ </template>
|
|
|
+ </Field> -->
|
|
|
+ </CellGroup>
|
|
|
+ </Form>
|
|
|
+ <div class="g-form__footer">
|
|
|
+ <template v-if="formData.BuyOrSell === BuyOrSell.Buy">
|
|
|
+ <Button type="danger" block square :disabled="!formData.OrderQty"
|
|
|
+ @click="onBeforeSubmit(BuildType.Open)" v-if="!quote?.iscannotbuy">{{ $t('quote.orderbuy') }}</Button>
|
|
|
+ <Button color="#199e00" block square
|
|
|
+ :disabled="!formData.OrderQty || !sellQty || (formData.OrderQty > sellQty)"
|
|
|
+ @click="onBeforeSubmit(BuildType.Close)" v-if="!isTrademode16">
|
|
|
+ <span>{{ $t('quote.transferbuy') }}</span>
|
|
|
+ <span v-if="sellQty">(≤{{ sellQty }})</span>
|
|
|
+ </Button>
|
|
|
+ </template>
|
|
|
+ <template v-if="formData.BuyOrSell === BuyOrSell.Sell">
|
|
|
+ <Button type="danger" block square :disabled="!formData.OrderQty"
|
|
|
+ @click="onBeforeSubmit(BuildType.Open)"
|
|
|
+ v-if="!isTrademode16 && !quote?.iscannotsell">{{ $t('quote.ordersell') }}</Button>
|
|
|
+ <Button color="#199e00" block square
|
|
|
+ :disabled="!formData.OrderQty || !buyQty || (formData.OrderQty > buyQty)"
|
|
|
+ @click="onBeforeSubmit(BuildType.Close)">
|
|
|
+ <span>{{ $t('quote.transfersell') }}</span>
|
|
|
+ <span v-if="buyQty">(≤{{ buyQty }})</span>
|
|
|
+ </Button>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <component ref="componentRef" :is="componentMap.get(componentId)" v-bind="{ goodsCode, goodsid}"
|
|
|
+ @closed="closeComponent" v-if="componentId" />
|
|
|
+ </template>
|
|
|
+ </app-view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { useFuturesStore, usePositionStore, i18n } from '@/stores'
|
|
|
+import { useNavigation } from '@mobile/router/navigation'
|
|
|
+import { shallowRef, onMounted, computed, defineAsyncComponent } from 'vue'
|
|
|
+import { Form, Field, Button, FieldRule, FormInstance, Radio, RadioGroup, CellGroup, Icon } from 'vant'
|
|
|
+import { useOrder } from '@/business/trade'
|
|
|
+import { BuyOrSell, getBuyOrSellList, BuildType, getPricemode2List, PriceMode } from '@/constants/order'
|
|
|
+import { useComponent } from '@/hooks/component'
|
|
|
+import { fullloading, dialog } from '@/utils/vant'
|
|
|
+import Stepper from '@mobile/components/base/stepper/index.vue'
|
|
|
+import { formatDecimal, handleNumberValue } from '@/filters'
|
|
|
+
|
|
|
+const { router, getQueryString, getQueryStringToNumber } = useNavigation()
|
|
|
+const { global: { t } } = i18n
|
|
|
+
|
|
|
+const goodsCode = getQueryString('goodscode') ?? ''
|
|
|
+const goodsid = getQueryStringToNumber('goodsid')
|
|
|
+const futuresStore = useFuturesStore()
|
|
|
+const quote = futuresStore.getGoodsQuote(goodsCode)
|
|
|
+// 小数位以及步进值
|
|
|
+const { decimalplace = 0.0 } = quote.value ?? {}
|
|
|
+const quoteminunit = quote.value?.quoteminunit ?? 1.0
|
|
|
+const decimalvalue = Math.pow(10.0, -decimalplace)*(quoteminunit == 0 ? 1 : quoteminunit)
|
|
|
+const isTrademode16 = computed(() => quote.value?.trademode === 16)
|
|
|
+
|
|
|
+const { formData, formSubmit } = useOrder()
|
|
|
+const formRef = shallowRef<FormInstance>()
|
|
|
+const qtyStepList = [1, 5, 10, 20, 30, 50] // 数量步长列表
|
|
|
+const qtyStep = shallowRef(qtyStepList[0]) // 数量步长
|
|
|
+
|
|
|
+// const accountStore = useAccountStore()
|
|
|
+const positionStore = usePositionStore()
|
|
|
+// 持仓汇总
|
|
|
+const position = shallowRef<Model.TradePositionRsp[]>([])
|
|
|
+
|
|
|
+const { componentRef, componentId, openComponent, closeComponent } = useComponent()
|
|
|
+const componentMap = new Map<string, unknown>([
|
|
|
+ ['detail', defineAsyncComponent(() => import('./components/detail/Index.vue'))],
|
|
|
+])
|
|
|
+
|
|
|
+// // const sl = shallowRef(false) // 止损
|
|
|
+// // const sp = shallowRef(false) // 止盈
|
|
|
+
|
|
|
+// const total = computed(() => {
|
|
|
+// const { avaiableMoney = 0 } = accountStore.currentAccount
|
|
|
+// const { marketmarginalgorithm = 0, marketmarginvalue = 0, agreeunit = 0 } = quote.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 marketPrice = computed(() => {
|
|
|
+ const { ask = 0, bid = 0 } = quote.value ?? {}
|
|
|
+ return formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
|
|
|
+})
|
|
|
+
|
|
|
+// 买方向持仓数量
|
|
|
+const buyQty = computed(() => positionStore.getOrderQty(BuyOrSell.Buy, goodsCode))
|
|
|
+// 卖方向持仓数量
|
|
|
+const sellQty = computed(() => positionStore.getOrderQty(BuyOrSell.Sell, goodsCode))
|
|
|
+
|
|
|
+// 表单验证规则
|
|
|
+const formRules: { [key in keyof Proto.OrderReq]?: FieldRule[] } = {
|
|
|
+ OrderQty: [{
|
|
|
+ message: t('quote.pricing.tips1'),
|
|
|
+ validator: () => {
|
|
|
+ return !!formData.OrderQty
|
|
|
+ }
|
|
|
+ }],
|
|
|
+ // 限价
|
|
|
+ MarketMaxSub: [{
|
|
|
+ message: t('quote.pricing.tips3'),
|
|
|
+ validator: () => {
|
|
|
+ if (formData.PriceMode === PriceMode.Market) {
|
|
|
+ return !!formData.MarketMaxSub
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }],
|
|
|
+ // 限价
|
|
|
+ OrderPrice: [{
|
|
|
+ message: t('quote.pricing.tips2'),
|
|
|
+ validator: () => {
|
|
|
+ if (formData.PriceMode === PriceMode.Limit) {
|
|
|
+ return !!formData.OrderPrice
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }],
|
|
|
+// // SlPrice: [{
|
|
|
+// // message: '请输入止损价',
|
|
|
+// // validator: () => {
|
|
|
+// // if (sl.value) {
|
|
|
+// // return !!formData.SlPrice
|
|
|
+// // }
|
|
|
+// // return true
|
|
|
+// // }
|
|
|
+// // }],
|
|
|
+// // SpPrice: [{
|
|
|
+// // message: '请输入止盈价',
|
|
|
+// // validator: () => {
|
|
|
+// // if (sp.value) {
|
|
|
+// // return !!formData.SpPrice
|
|
|
+// // }
|
|
|
+// // return true
|
|
|
+// // }
|
|
|
+// // }],
|
|
|
+}
|
|
|
+
|
|
|
+const onBuyOrSellChanged = () => {
|
|
|
+ if (formData.PriceMode === PriceMode.Limit) {
|
|
|
+ const { ask = 0, bid = 0 } = quote.value ?? {}
|
|
|
+ formData.OrderPrice = formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const onPriceModeChanged = () => {
|
|
|
+ if (formData.PriceMode === PriceMode.Limit) {
|
|
|
+ const { ask = 0, bid = 0 } = quote.value ?? {}
|
|
|
+ formData.OrderPrice = formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const onBeforeSubmit = (buildType: BuildType) => {
|
|
|
+ formData.BuildType = buildType
|
|
|
+ if (buildType === BuildType.Close) {
|
|
|
+ const { ask = 0, bid = 0 } = quote.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
|
|
|
+ }
|
|
|
+ }
|
|
|
+ formRef.value?.submit()
|
|
|
+}
|
|
|
+
|
|
|
+// 委托下单
|
|
|
+const onSubmit = () => {
|
|
|
+ const { goodsid, marketid } = quote.value ?? {}
|
|
|
+ formData.GoodsID = goodsid
|
|
|
+ formData.MarketID = marketid
|
|
|
+ // 市价
|
|
|
+ if (formData.PriceMode === PriceMode.Market) { formData.OrderPrice = marketPrice.value }
|
|
|
+ // 限价
|
|
|
+ // if (!sl.value) { formData.SlPrice = undefined }
|
|
|
+ // if (!sp.value) { formData.SpPrice = undefined }
|
|
|
+
|
|
|
+ fullloading((hideLoading) => {
|
|
|
+ formSubmit().then(() => {
|
|
|
+ hideLoading()
|
|
|
+ dialog(t('common.submitsuccess')).then(() => {
|
|
|
+ router.back()
|
|
|
+ })
|
|
|
+ }).catch((err) => {
|
|
|
+ hideLoading(err, 'fail')
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const onRadioChange = (value: number) => {
|
|
|
+ formData.OrderQty = value
|
|
|
+}
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ formData.BuyOrSell = BuyOrSell.Buy
|
|
|
+ formData.BuildType = BuildType.Open
|
|
|
+ formData.PriceMode = PriceMode.Market
|
|
|
+ formData.MarketMaxSub = 100.0
|
|
|
+ /// 默认价格和数量
|
|
|
+ const { last = 0, presettle = 0 } = quote.value ?? {}
|
|
|
+ formData.OrderPrice = last || presettle
|
|
|
+ formData.OrderQty = qtyStep.value
|
|
|
+ })
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less">
|
|
|
+@import './index.less';
|
|
|
+</style>
|