|
|
@@ -0,0 +1,292 @@
|
|
|
+<template>
|
|
|
+ <app-view class="pricing-trade">
|
|
|
+ <template #header>
|
|
|
+ <app-navbar :title="quote ? `${quote.goodscode}` : $t('quote.pricing.title')" >
|
|
|
+ <template #right>
|
|
|
+ <Icon v-if="system_1012 != '0'" name="bars" size="20px" @click="openComponent('detail')" />
|
|
|
+ </template>
|
|
|
+ </app-navbar>
|
|
|
+ </template>
|
|
|
+ <div class="pricing-trade__form" v-if="quote">
|
|
|
+ <div class="form-buyorsell">
|
|
|
+ <Tabs class="van-tabs--list" v-model:active="formData.BuyOrSell" >
|
|
|
+ <Tab :title="$t('quote.bid')" :name="BuyOrSell.Buy"></Tab>
|
|
|
+ <Tab :title="$t('quote.ask')" :name="BuyOrSell.Sell"></Tab>
|
|
|
+ </Tabs>
|
|
|
+ </div>
|
|
|
+ <div class="form-price">
|
|
|
+ <dl v-if="formData.BuyOrSell === BuyOrSell.Buy">
|
|
|
+ <dd :class="quote.bidColor">{{ handleNumberValue(formatDecimal(quote.ask, quote.decimalplace)) }}
|
|
|
+ </dd>
|
|
|
+ </dl>
|
|
|
+ <dl v-if="formData.BuyOrSell === BuyOrSell.Sell">
|
|
|
+ <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.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 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>
|
|
|
+ </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('operation.order') }}</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('operation.order') }}</Button>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <Tabs class="van-tabs--list" v-model:active="active" >
|
|
|
+ <template v-for="(item, index) in components.filter(e => e.show === true)" :key="index">
|
|
|
+ <Tab :title="item.title" :name="item.name">
|
|
|
+ <component :is="item.component" v-bind="{ goodsCode, goodsid, fromTrade, pictureurl }" @callBack="itemBack"/>
|
|
|
+ </Tab>
|
|
|
+ </template>
|
|
|
+ </Tabs>
|
|
|
+ <template #footer>
|
|
|
+ <component ref="componentRef" :is="componentMap.get(componentId)" v-bind="{ goodsCode, goodsid, selectedRow }"
|
|
|
+ @closed="closeComponent" v-if="componentId" />
|
|
|
+ </template>
|
|
|
+ </app-view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { useFuturesStore, useUserStore, 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, Tab, Tabs } from 'vant'
|
|
|
+import { useOrder } from '@/business/trade'
|
|
|
+import { BuyOrSell, BuildType, getPricemode2List, PriceMode } from '@/constants/order'
|
|
|
+import { useComponent } from '@/hooks/component'
|
|
|
+import { fullloading, dialog } from '@/utils/vant'
|
|
|
+import { formatDecimal, handleNumberValue } from '@/filters'
|
|
|
+import Stepper from '@mobile/components/base/stepper/index.vue'
|
|
|
+import eventBus from '@/services/bus'
|
|
|
+
|
|
|
+const { getQueryString, getQueryStringToNumber } = useNavigation()
|
|
|
+const { global: { t } } = i18n
|
|
|
+
|
|
|
+const goodsCode = getQueryString('goodscode') ?? ''
|
|
|
+const goodsid = getQueryStringToNumber('goodsid')
|
|
|
+const buyOrSell = getQueryStringToNumber('buyOrSell')
|
|
|
+const buildType = getQueryStringToNumber('buildType')
|
|
|
+const orderQty = getQueryStringToNumber('orderQty')
|
|
|
+const futuresStore = useFuturesStore()
|
|
|
+const quote = futuresStore.getGoodsQuote(goodsCode)
|
|
|
+const { pictureurl } = futuresStore.getGoods(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 selectedRow = shallowRef<Model.SBYJMyOrderRsp>()
|
|
|
+
|
|
|
+const { getSystemParamValue } = useUserStore()
|
|
|
+const system_1012 = getSystemParamValue('1012') ?? '1'
|
|
|
+
|
|
|
+const { formData, formSubmit } = useOrder()
|
|
|
+const formRef = shallowRef<FormInstance>()
|
|
|
+// 数量步长列表
|
|
|
+const qtyStepList = computed(() => {
|
|
|
+ const system_1009 = Number(getSystemParamValue('1009')) ?? 1
|
|
|
+ return [1*system_1009, 5*system_1009, 10*system_1009, 20*system_1009, 30*system_1009, 50*system_1009]
|
|
|
+})
|
|
|
+const qtyStep = shallowRef(qtyStepList.value[0]) // 数量步长
|
|
|
+const fromTrade = true
|
|
|
+
|
|
|
+// 持仓汇总
|
|
|
+const position = shallowRef<Model.TradePositionRsp[]>([])
|
|
|
+
|
|
|
+// 点击返回
|
|
|
+const itemBack = (isPosition: number, buyorsell: BuyOrSell, tradeId: string, item: Model.SBYJMyOrderRsp) => {
|
|
|
+ formData.BuyOrSell = buyorsell === BuyOrSell.Buy ? BuyOrSell.Sell : BuyOrSell.Buy
|
|
|
+ if (isPosition === 2) {
|
|
|
+ selectedRow.value = item
|
|
|
+ // 打开退订
|
|
|
+ openComponent('transfer')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const { componentRef, componentId, openComponent, closeComponent } = useComponent()
|
|
|
+
|
|
|
+const active = shallowRef(pictureurl != '' ? 'images' : 'cancel')
|
|
|
+
|
|
|
+const components = [
|
|
|
+ {
|
|
|
+ name: 'images',
|
|
|
+ title: t('operation.details'),
|
|
|
+ component: defineAsyncComponent(() => import('../images/Index.vue')),
|
|
|
+ show: pictureurl != ''
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'cancel',
|
|
|
+ title: t('quote.pricing.ordercancel'),
|
|
|
+ component: defineAsyncComponent(() => import('../components/cancel/Index.vue')),
|
|
|
+ show: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'position',
|
|
|
+ title: t('quote.pricing.position'),
|
|
|
+ component: defineAsyncComponent(() => import('@mobile/views/order/position/components/pricing/list/Index.vue')),
|
|
|
+ show: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'holdlb2',
|
|
|
+ title: t('quote.pricing.orderdetails'),
|
|
|
+ component: defineAsyncComponent(() => import('../holdlb2/Index.vue')),
|
|
|
+ detail: defineAsyncComponent(() => import('@mobile/views/order/position/components/pricing/detail2/Index.vue')),
|
|
|
+ show: true
|
|
|
+ }
|
|
|
+]
|
|
|
+
|
|
|
+const componentMap = new Map<string, unknown>([
|
|
|
+ ['detail', defineAsyncComponent(() => import('../components/detail/Index.vue'))],
|
|
|
+ ['transfer', defineAsyncComponent(() => import('@mobile/views/order/position/components/pricing/detail2/components/transfer/Index.vue'))]
|
|
|
+])
|
|
|
+
|
|
|
+// 计算市价
|
|
|
+const marketPrice = computed(() => {
|
|
|
+ const { ask = 0, bid = 0 } = quote.value ?? {}
|
|
|
+ return formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
|
|
|
+})
|
|
|
+
|
|
|
+// 表单验证规则
|
|
|
+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: (val) => {
|
|
|
+ if (formData.PriceMode === PriceMode.Market) {
|
|
|
+ if (val < 0) {
|
|
|
+ return t('quote.pricing.tips3')
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return !!formData.MarketMaxSub
|
|
|
+ }
|
|
|
+ }],
|
|
|
+ // 限价
|
|
|
+ OrderPrice: [{
|
|
|
+ message: t('quote.pricing.tips2'),
|
|
|
+ validator: () => {
|
|
|
+ if (formData.PriceMode === PriceMode.Limit) {
|
|
|
+ return !!formData.OrderPrice
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }],
|
|
|
+}
|
|
|
+
|
|
|
+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
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ formData.RelatedID = undefined
|
|
|
+ }
|
|
|
+ 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 }
|
|
|
+
|
|
|
+ fullloading((hideLoading) => {
|
|
|
+ formSubmit().then(() => {
|
|
|
+ hideLoading()
|
|
|
+ dialog({ message: t('common.submitsuccess'), confirmButtonText: t('operation.confirm') }).then(() => {
|
|
|
+ // 成交通知
|
|
|
+ eventBus.$emit('OrderDealedNtf')
|
|
|
+ })
|
|
|
+ }).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
|
|
|
+
|
|
|
+ /// 如果是从头寸点进来的
|
|
|
+ if ( buildType === BuildType.Close) {
|
|
|
+ formData.OrderQty = orderQty
|
|
|
+ formData.BuyOrSell = buyOrSell
|
|
|
+ formData.BuildType = buildType
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less">
|
|
|
+@import './index.less';
|
|
|
+</style>
|