|
|
@@ -0,0 +1,255 @@
|
|
|
+<template>
|
|
|
+ <app-view class="listing-goods-detail g-layout g-form">
|
|
|
+ <template #header>
|
|
|
+ <app-navbar :title="quote ? quote.goodscode : '交易'">
|
|
|
+ <template #footer>
|
|
|
+ <Cell>
|
|
|
+ <template #title v-if="quote">
|
|
|
+ <h3 :class="quote.lastColor">
|
|
|
+ <span>{{ handleNumberValue(quote.last.toFixed(quote.decimalplace)) }}</span>
|
|
|
+ </h3>
|
|
|
+ <span :class="quote.lastColor">{{ parsePercent(quote.change) }}</span>
|
|
|
+ </template>
|
|
|
+ <template #right-icon>
|
|
|
+ <span @click="navigateToGoodsChart">图表</span>
|
|
|
+ </template>
|
|
|
+ </Cell>
|
|
|
+ </template>
|
|
|
+ </app-navbar>
|
|
|
+ </template>
|
|
|
+ <component :is="Tik" v-bind="{ goodsCode }" />
|
|
|
+ <Row class="g-layout-block g-layout-block--inset" gutter="10">
|
|
|
+ <Col span="12">
|
|
|
+ <Button :type="formData.BuyOrSell === BuyOrSell.Buy ? 'success' : 'default'" size="small" block
|
|
|
+ @click="formData.BuyOrSell = BuyOrSell.Buy">买入</Button>
|
|
|
+ </Col>
|
|
|
+ <Col span="12">
|
|
|
+ <Button :type="formData.BuyOrSell === BuyOrSell.Sell ? 'danger' : 'default'" size="small" block
|
|
|
+ @click="formData.BuyOrSell = BuyOrSell.Sell">卖出</Button>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ <Form ref="formRef" class="g-form__container" @submit="onSubmit">
|
|
|
+ <CellGroup inset>
|
|
|
+ <Field label="类型" is-link>
|
|
|
+ <template #input>
|
|
|
+ <app-select v-model="formData.PriceMode" :options="getPricemode2List()" />
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <Field label="价格" :rules="formRules.OrderPrice" v-if="formData.PriceMode === PriceMode.Limit">
|
|
|
+ <template #input>
|
|
|
+ <app-stepper v-model="formData.OrderPrice" :min="0" :decimal-length="quote?.decimalplace"
|
|
|
+ :step="quote?.decimalvalue" />
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <Cell title="价格" value="最优市价" v-if="formData.PriceMode === PriceMode.Market" />
|
|
|
+ <Field label="数量" :rules="formRules.OrderQty">
|
|
|
+ <template #input>
|
|
|
+ <app-stepper v-model="formData.OrderQty" :min="0"
|
|
|
+ :decimal-length="baseAccount?.currencydecimalplace" />
|
|
|
+ </template>
|
|
|
+ </Field>
|
|
|
+ <Cell :title="formData.BuyOrSell === BuyOrSell.Buy ? '预估支付' : '预估获取'"
|
|
|
+ :value="formatDecimal(calculations.estimatedAmount, quoteAccount?.currencydecimalplace)" />
|
|
|
+ </CellGroup>
|
|
|
+ <CellGroup inset v-if="formData.BuyOrSell === BuyOrSell.Buy">
|
|
|
+ <Cell title="预估手续费"
|
|
|
+ :value="formatDecimal(calculations.buyEstimatedFee, quoteAccount?.currencydecimalplace)" />
|
|
|
+ <Cell title="可用余额"
|
|
|
+ :value="formatDecimal(calculations.maxBalance, quoteAccount?.currencydecimalplace)" />
|
|
|
+ <Cell title="可买数量" :value="formatDecimal(calculations.maxBuyQty, baseAccount?.currencydecimalplace)" />
|
|
|
+ </CellGroup>
|
|
|
+ <CellGroup inset v-if="formData.BuyOrSell === BuyOrSell.Sell">
|
|
|
+ <Cell title="预估手续费"
|
|
|
+ :value="formatDecimal(calculations.sellEstimatedFee, quoteAccount?.currencydecimalplace)" />
|
|
|
+ <Cell title="可获金额" :value="formatDecimal(calculations.maxAmount, quoteAccount?.currencydecimalplace)" />
|
|
|
+ <Cell title="可卖数量" :value="formatDecimal(calculations.maxSellQty, baseAccount?.currencydecimalplace)" />
|
|
|
+ </CellGroup>
|
|
|
+ </Form>
|
|
|
+ <Row class="g-layout-block g-layout-block--inset">
|
|
|
+ <Col span="24">
|
|
|
+ <Button type="success" block v-if="formData.BuyOrSell === BuyOrSell.Buy"
|
|
|
+ @click="formRef?.submit">买入</Button>
|
|
|
+ <Button type="danger" block v-if="formData.BuyOrSell === BuyOrSell.Sell"
|
|
|
+ @click="formRef?.submit">卖出</Button>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ <Tabs v-model:active="tabIndex" sticky>
|
|
|
+ <Tab :title="t('digital.order')">
|
|
|
+ <spot-order :params="{ goodsid: goodsId, orderstatuses: '3,7' }" />
|
|
|
+ </Tab>
|
|
|
+ <Tab :title="t('digital.account')">
|
|
|
+ <spot-account v-bind="{ goodsId }" />
|
|
|
+ </Tab>
|
|
|
+ </Tabs>
|
|
|
+ </app-view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { shallowRef, reactive, computed, onMounted, onUnmounted, onActivated, defineAsyncComponent } from 'vue'
|
|
|
+import { Form, Button, CellGroup, Field, Cell, Tab, Tabs, Col, Row, FormInstance, showDialog, FieldRule } from 'vant'
|
|
|
+import { fullloading } from '@/utils/vant'
|
|
|
+import { parsePercent, formatDecimal,handleNumberValue } from '@/filters'
|
|
|
+import { EBuildType, EDelistingType, EListingSelectType, EOrderOperateType, EValidType } from '@/constants/client'
|
|
|
+import { BuyOrSell, PriceMode, getPricemode2List } from '@/constants/order'
|
|
|
+import { useNavigation } from '@mobile/router/navigation'
|
|
|
+import { digitalOrder } from '@/services/api/digital'
|
|
|
+import { i18n, useFuturesStore, useUserStore } from '@/stores'
|
|
|
+import { useSpotAccountStore } from '../../../wallet/components/spot/composables'
|
|
|
+import { useComponent } from '@/hooks/component'
|
|
|
+import quoteSocket from '@/services/websocket/quote'
|
|
|
+import Long from 'long'
|
|
|
+import AppSelect from '@mobile/components/base/select/index.vue'
|
|
|
+import AppStepper from '@mobile/components/base/stepper/index.vue'
|
|
|
+import SpotOrder from '../../../spot/components/order/index.vue'
|
|
|
+import SpotAccount from '../../../spot/components/account/index.vue'
|
|
|
+
|
|
|
+const Tik = defineAsyncComponent(() => import('@mobile/components/modules/quote/tik/index.vue'))
|
|
|
+const { componentRef, componentId, openComponent, closeComponent } = useComponent()
|
|
|
+
|
|
|
+const subscribe = quoteSocket.createSubscribe()
|
|
|
+
|
|
|
+const { global: { t } } = i18n
|
|
|
+
|
|
|
+const { router, getQueryStringToNumber, getGlobalUrlParams } = useNavigation()
|
|
|
+const goodsId = getQueryStringToNumber('id')
|
|
|
+const userStore = useUserStore()
|
|
|
+const futuresStore = useFuturesStore()
|
|
|
+const spotAccountStore = useSpotAccountStore()
|
|
|
+const tabIndex = shallowRef(0)
|
|
|
+const formRef = shallowRef<FormInstance>()
|
|
|
+
|
|
|
+const formData = reactive<Partial<Proto.DigitalOrderReq>>({
|
|
|
+ BuyOrSell: BuyOrSell.Buy,
|
|
|
+ PriceMode: PriceMode.Market,
|
|
|
+ OperateType: EOrderOperateType.ORDEROPERATETYPE_NORMAL,
|
|
|
+ ListingSelectType: EListingSelectType.LISTINGSELECTTYPE_DELISTING,
|
|
|
+ DelistingType: EDelistingType.DELISTINGTYPE_PRICE,
|
|
|
+ BuildType: EBuildType.BUILDTYPE_OPEN,
|
|
|
+ TimevalidType: EValidType.VALIDTYPE_DR,
|
|
|
+ OrderFlag: 1,
|
|
|
+ OrderQty: 1,
|
|
|
+})
|
|
|
+
|
|
|
+const quote = computed(() => futuresStore.getQuoteItem({ goodsid: goodsId }))
|
|
|
+const baseAccount = computed(() => spotAccountStore.getAccountItem({ currencyid: quote.value?.goodscurrencyid })) // 基础货币账户
|
|
|
+const quoteAccount = computed(() => spotAccountStore.getAccountItem({ currencyid: quote.value?.currencyid })) // 计价货币账户
|
|
|
+
|
|
|
+const goodsCode = computed(() => quote.value?.goodscode ?? '')
|
|
|
+
|
|
|
+const calculations = computed(() => {
|
|
|
+ const buyFeeValue = futuresStore.getFeeValue(quote.value, 101)
|
|
|
+ const sellFeeValue = futuresStore.getFeeValue(quote.value, 102)
|
|
|
+
|
|
|
+ const { last = 0, agreeunit = 0 } = quote.value ?? {}
|
|
|
+ const { OrderPrice = 0, OrderQty = 0 } = formData
|
|
|
+
|
|
|
+ const price = formData.PriceMode === PriceMode.Market ? last : OrderPrice
|
|
|
+ const amount = OrderQty * agreeunit
|
|
|
+
|
|
|
+ // 预估金额
|
|
|
+ const estimatedAmount = price * amount
|
|
|
+
|
|
|
+ // 可用余额
|
|
|
+ const maxBalance = spotAccountStore.getAvailableBalance(quoteAccount.value)
|
|
|
+ // 预估手续费
|
|
|
+ const buyEstimatedFee = (buyFeeValue.FeeAlgorithm === 2 ? amount : estimatedAmount) * buyFeeValue.feeValue
|
|
|
+ // 可买数量
|
|
|
+ const maxBuyQty = price ? (maxBalance - buyEstimatedFee) / (price * agreeunit) : 0
|
|
|
+
|
|
|
+ // 可卖数量
|
|
|
+ const maxSellQty = spotAccountStore.getAvailableBalance(baseAccount.value)
|
|
|
+ // 可获金额
|
|
|
+ const maxAmount = price * maxSellQty * agreeunit
|
|
|
+ // 预估手续费
|
|
|
+ const sellEstimatedFee = (sellFeeValue.FeeAlgorithm === 2 ? amount : estimatedAmount) * sellFeeValue.feeValue
|
|
|
+
|
|
|
+ return {
|
|
|
+ estimatedAmount,
|
|
|
+ maxBalance,
|
|
|
+ maxBuyQty,
|
|
|
+ buyEstimatedFee,
|
|
|
+ maxAmount,
|
|
|
+ maxSellQty,
|
|
|
+ sellEstimatedFee
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 表单验证规则
|
|
|
+const formRules: { [key: string]: FieldRule[] } = {
|
|
|
+ OrderPrice: [{
|
|
|
+ message: '请输入价格',
|
|
|
+ validator: () => {
|
|
|
+ return Number(formData.OrderPrice) > 0
|
|
|
+ }
|
|
|
+ }],
|
|
|
+ OrderQty: [{
|
|
|
+ message: '请输入数量',
|
|
|
+ validator: () => Number(formData.OrderQty) > 0
|
|
|
+ }],
|
|
|
+}
|
|
|
+
|
|
|
+const navigateToGoodsChart = () => {
|
|
|
+ router.push({
|
|
|
+ name: 'spot-goods-chart',
|
|
|
+ query: {
|
|
|
+ id: goodsId
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const onSubmit = () => {
|
|
|
+ const baseAccountId = baseAccount.value?.digitalaccountid
|
|
|
+ const quoteAccountId = quoteAccount.value?.digitalaccountid
|
|
|
+
|
|
|
+ if (baseAccountId && quoteAccountId) {
|
|
|
+ fullloading((hideLoading) => {
|
|
|
+ formData.BaseAccountID = Long.fromString(baseAccountId)
|
|
|
+ formData.QuoteAccountID = Long.fromString(quoteAccountId)
|
|
|
+ formData.GoodsID = quote.value?.goodsid
|
|
|
+ formData.MarketID = quote.value?.marketid
|
|
|
+ // 市价
|
|
|
+ if (formData.PriceMode === PriceMode.Market) {
|
|
|
+ const param112 = userStore.getSystemParamValue('112')
|
|
|
+ formData.OrderPrice = quote.value?.last
|
|
|
+ formData.MarketMaxSub = Number(param112) || 100
|
|
|
+ }
|
|
|
+
|
|
|
+ digitalOrder({
|
|
|
+ data: formData
|
|
|
+ }).then(() => {
|
|
|
+ hideLoading('提交成功', 'success')
|
|
|
+ }).catch((err) => {
|
|
|
+ hideLoading(err, 'fail')
|
|
|
+ })
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ showDialog({
|
|
|
+ message: '请先激活资金账户'
|
|
|
+ }).then(() => {
|
|
|
+ tabIndex.value = 1
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ if (quote.value) {
|
|
|
+ subscribe.start(quote.value.goodscode)
|
|
|
+ formData.OrderPrice = quote.value.last
|
|
|
+ }
|
|
|
+
|
|
|
+ onActivated(() => {
|
|
|
+ const params = getGlobalUrlParams()
|
|
|
+ if (params.buyOrSell !== undefined) {
|
|
|
+ formData.BuyOrSell = params.buyOrSell
|
|
|
+ }
|
|
|
+ })
|
|
|
+})
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ subscribe.stop()
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less">
|
|
|
+@import './index.less';
|
|
|
+</style>
|