Index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. <template>
  2. <app-view class="pricing-trade">
  3. <template #header>
  4. <app-navbar :title="quote ? `${quote.goodscode}` : $t('quote.pricing.title')" >
  5. <template #right>
  6. <Icon v-if="system_1012 != '0'" name="bars" size="20px" @click="openComponent('detail')" />
  7. </template>
  8. </app-navbar>
  9. </template>
  10. <div class="pricing-trade__form" v-if="quote">
  11. <div class="form-price">
  12. <dl>
  13. <dt>{{ t('quote.bid') }}</dt>
  14. <dd :class="quote.bidColor">{{ handleNumberValue(formatDecimal(quote.ask, quote.decimalplace)) }}
  15. </dd>
  16. </dl>
  17. <dl>
  18. <dt>{{ t('quote.ask') }}</dt>
  19. <dd :class="quote.askColor">{{ handleNumberValue(formatDecimal(quote.bid, quote.decimalplace)) }}
  20. </dd>
  21. </dl>
  22. </div>
  23. </div>
  24. <!-- 下单 -->
  25. <Form ref="formRef" class="g-form__container" @submit="onSubmit">
  26. <CellGroup inset>
  27. <Field :label="$t('quote.pricing.buyorsell')">
  28. <template #input>
  29. <RadioGroup v-model="formData.BuyOrSell" direction="horizontal" @click="onBuyOrSellChanged">
  30. <Radio v-for="(item, index) in getBuyOrSellList()" :key="index" :name="item.value">{{ item.label
  31. }}</Radio>
  32. </RadioGroup>
  33. </template>
  34. </Field>
  35. <Field :label="$t('quote.pricing.pricemode')">
  36. <template #input>
  37. <RadioGroup v-model="formData.PriceMode" direction="horizontal" @click="onPriceModeChanged">
  38. <Radio v-for="(item, index) in getPricemode2List()" :key="index" :name="item.value">{{
  39. item.label
  40. }}</Radio>
  41. </RadioGroup>
  42. </template>
  43. </Field>
  44. <Field name="OrderQty" :label="$t('quote.pricing.orderqty')">
  45. <template #input>
  46. <div class="g-qty-group">
  47. <div class="g-qty-group__stepper">
  48. <Stepper v-model="formData.OrderQty" theme="round" button-size="22" :min="0"
  49. :step="qtyStep" integer />
  50. </div>
  51. <RadioGroup v-model="qtyStep" direction="horizontal" @change="onRadioChange">
  52. <Radio v-for="(value, index) in qtyStepList" :key="index" :name="value">{{ value }}
  53. </Radio>
  54. </RadioGroup>
  55. </div>
  56. </template>
  57. </Field>
  58. <!-- 市价 -->
  59. <!-- <Field label="价格" v-if="formData.PriceMode === PriceMode.Market">
  60. <template #input>
  61. <span>{{ handleNumberValue(marketPrice) }}</span>
  62. </template>
  63. </Field> -->
  64. <!-- 允许成交范围 -->
  65. <!-- <Field name="MarketMaxSub" :rules="formRules.MarketMaxSub" :label="$t('quote.pricing.marketmaxsub')" v-if="formData.PriceMode === PriceMode.Market">
  66. <template #input>
  67. <Stepper v-model="formData.MarketMaxSub" :min="0" :max="999" theme="round" button-size="22" :auto-fixed="false" integer />
  68. </template>
  69. </Field> -->
  70. <!-- 限价 -->
  71. <Field name="OrderPrice" :rules="formRules.OrderPrice" :label="$t('quote.pricing.price')" v-if="formData.PriceMode === PriceMode.Limit">
  72. <template #input>
  73. <Stepper v-model="formData.OrderPrice" theme="round" button-size="22" :min="0"
  74. :auto-fixed="false" :decimal-length="decimalplace" :step="decimalvalue" />
  75. </template>
  76. </Field>
  77. <!-- <Field name="SlPrice" :rules="formRules.SlPrice"
  78. v-if="formData.PriceMode === PriceMode.Limit && formData.BuildType === BuildType.Open">
  79. <template #label>
  80. <Checkbox v-model="sl">止损</Checkbox>
  81. </template>
  82. <template #input>
  83. <Stepper v-model="formData.SlPrice" :disabled="!sl" theme="round" button-size="22" allow-empty
  84. :default-value="0" :min="0" :decimal-length="decimalplace" :step="decimalvalue" />
  85. </template>
  86. </Field>
  87. <Field name="SpPrice" :rules="formRules.SpPrice"
  88. v-if="formData.PriceMode === PriceMode.Limit && formData.BuildType === BuildType.Open">
  89. <template #label>
  90. <Checkbox v-model="sp">止盈</Checkbox>
  91. </template>
  92. <template #input>
  93. <Stepper v-model="formData.SpPrice" :disabled="!sp" theme="round" button-size="22" allow-empty
  94. :default-value="0" :min="0" :decimal-length="decimalplace" :step="decimalvalue" />
  95. </template>
  96. </Field> -->
  97. <!-- <template v-if="formData.BuyOrSell === BuyOrSell.Buy || quote?.tradeproperty !== 2">
  98. <Field :label="$t('quote.pricing.enableQty')">
  99. <template #input>
  100. <span>{{ total.enableQty }}</span>
  101. </template>
  102. </Field>
  103. <Field :label="$t('quote.pricing.deposit')">
  104. <template #input>
  105. <span>{{ total.deposit.toFixed(2) }}</span>
  106. </template>
  107. </Field>
  108. </template>
  109. <Field :label="$t('quote.pricing.avaiableMoney')">
  110. <template #input>
  111. <span>{{ accountStore.currentAccount.avaiableMoney?.toFixed(2) }}</span>
  112. </template>
  113. </Field> -->
  114. </CellGroup>
  115. </Form>
  116. <div class="g-form__footer">
  117. <template v-if="formData.BuyOrSell === BuyOrSell.Buy">
  118. <Button type="danger" block square :disabled="!formData.OrderQty"
  119. @click="onBeforeSubmit(BuildType.Open)" v-if="!quote?.iscannotbuy">{{ $t('operation.order') }}</Button>
  120. <!-- <Button color="#199e00" block square
  121. :disabled="!formData.OrderQty || !sellQty || (formData.OrderQty > sellQty)"
  122. @click="onBeforeSubmit(BuildType.Close)" v-if="!isTrademode16">
  123. <span>{{ $t('quote.transferbuy') }}</span>
  124. <span v-if="sellQty">(≤{{ sellQty }})</span>
  125. </Button> -->
  126. </template>
  127. <template v-if="formData.BuyOrSell === BuyOrSell.Sell">
  128. <Button type="danger" block square :disabled="!formData.OrderQty"
  129. @click="onBeforeSubmit(BuildType.Open)"
  130. v-if="!isTrademode16 && !quote?.iscannotsell">{{ $t('operation.order') }}</Button>
  131. <!-- <Button color="#199e00" block square
  132. :disabled="!formData.OrderQty || !buyQty || (formData.OrderQty > buyQty)"
  133. @click="onBeforeSubmit(BuildType.Close)">
  134. <span>{{ $t('quote.transfersell') }}</span>
  135. <span v-if="buyQty">(≤{{ buyQty }})</span>
  136. </Button> -->
  137. </template>
  138. </div>
  139. <Tabs class="van-tabs--list" v-model:active="active" >
  140. <template v-for="(item, index) in components.filter(e => e.show === true)" :key="index">
  141. <Tab :title="item.title" :name="item.name">
  142. <component :is="item.component" v-bind="{ goodsCode, goodsid, fromTrade, pictureurl }" @callBack="itemBack"/>
  143. </Tab>
  144. </template>
  145. </Tabs>
  146. <template #footer>
  147. <component ref="componentRef" :is="componentMap.get(componentId)" v-bind="{ goodsCode, goodsid, selectedRow }"
  148. @closed="closeComponent" v-if="componentId" />
  149. </template>
  150. </app-view>
  151. </template>
  152. <script lang="ts" setup>
  153. import { useFuturesStore, useUserStore, i18n } from '@/stores'
  154. import { useNavigation } from '@mobile/router/navigation'
  155. import { shallowRef, onMounted, computed, defineAsyncComponent } from 'vue'
  156. import { Form, Field, Button, FieldRule, FormInstance, Radio, RadioGroup, CellGroup, Icon, Tab, Tabs } from 'vant'
  157. import { useOrder } from '@/business/trade'
  158. import { BuyOrSell, getBuyOrSellList, BuildType, getPricemode2List, PriceMode } from '@/constants/order'
  159. import { useComponent } from '@/hooks/component'
  160. import { fullloading, dialog } from '@/utils/vant'
  161. import { formatDecimal, handleNumberValue } from '@/filters'
  162. // import service from '@/services'
  163. import Stepper from '@mobile/components/base/stepper/index.vue'
  164. import eventBus from '@/services/bus'
  165. const { getQueryString, getQueryStringToNumber } = useNavigation()
  166. const { global: { t } } = i18n
  167. const goodsCode = getQueryString('goodscode') ?? ''
  168. const goodsid = getQueryStringToNumber('goodsid')
  169. const buyOrSell = getQueryStringToNumber('buyOrSell')
  170. const buildType = getQueryStringToNumber('buildType')
  171. const orderQty = getQueryStringToNumber('orderQty')
  172. const futuresStore = useFuturesStore()
  173. const quote = futuresStore.getGoodsQuote(goodsCode)
  174. const { pictureurl } = futuresStore.getGoods(goodsCode) ?? {}
  175. // 小数位以及步进值
  176. const { decimalplace = 0.0 } = quote.value ?? {}
  177. const quoteminunit = quote.value?.quoteminunit ?? 1.0
  178. const decimalvalue = Math.pow(10.0, -decimalplace)*(quoteminunit == 0 ? 1 : quoteminunit)
  179. const isTrademode16 = computed(() => quote.value?.trademode === 16)
  180. const selectedRow = shallowRef<Model.SBYJMyOrderRsp>()
  181. const { getSystemParamValue } = useUserStore()
  182. const system_1012 = getSystemParamValue('1012') ?? '1'
  183. // const accountStore = useAccountStore()
  184. // const positionStore = usePositionStore()
  185. const { formData, formSubmit } = useOrder()
  186. const formRef = shallowRef<FormInstance>()
  187. // 数量步长列表
  188. const qtyStepList = computed(() => {
  189. const system_1009 = Number(getSystemParamValue('1009')) ?? 1
  190. return [1*system_1009, 5*system_1009, 10*system_1009, 20*system_1009, 30*system_1009, 50*system_1009]
  191. })
  192. const qtyStep = shallowRef(qtyStepList.value[0]) // 数量步长
  193. const fromTrade = true
  194. // 持仓汇总
  195. const position = shallowRef<Model.TradePositionRsp[]>([])
  196. // const oem = service.getConfig('oem')
  197. // 点击返回
  198. const itemBack = (isPosition: number, buyorsell: BuyOrSell, tradeId: string, item: Model.SBYJMyOrderRsp) => {
  199. formData.BuyOrSell = buyorsell === BuyOrSell.Buy ? BuyOrSell.Sell : BuyOrSell.Buy
  200. if (isPosition === 2) {
  201. // formData.RelatedID = handleRequestBigNumber(tradeId)
  202. selectedRow.value = item
  203. // 打开退订
  204. openComponent('transfer')
  205. }
  206. }
  207. const { componentRef, componentId, openComponent, closeComponent } = useComponent()
  208. const active = shallowRef(pictureurl != '' ? 'images' : 'cancel')
  209. const components = [
  210. {
  211. name: 'images',
  212. title: t('operation.details'),
  213. component: defineAsyncComponent(() => import('./images/Index.vue')),
  214. show: pictureurl != ''
  215. },
  216. {
  217. name: 'cancel',
  218. title: t('quote.pricing.ordercancel'),
  219. component: defineAsyncComponent(() => import('../trade/components/cancel/Index.vue')),
  220. show: true
  221. },
  222. {
  223. name: 'position',
  224. title: t('quote.pricing.position'),
  225. component: defineAsyncComponent(() => import('@mobile/views/order/position/components/pricing/list/Index.vue')),
  226. show: true
  227. },
  228. // {
  229. // name: 'holdlb',
  230. // title: t('quote.pricing.holdlb'),
  231. // component: defineAsyncComponent(() => import('../trade/holdlb/Index.vue')),
  232. // },
  233. {
  234. name: 'holdlb2',
  235. title: t('quote.pricing.orderdetails'),
  236. component: defineAsyncComponent(() => import('../trade/holdlb2/Index.vue')),
  237. detail: defineAsyncComponent(() => import('@mobile/views/order/position/components/pricing/detail2/Index.vue')),
  238. show: true
  239. }]
  240. const componentMap = new Map<string, unknown>([
  241. ['detail', defineAsyncComponent(() => import('./components/detail/Index.vue'))],
  242. ['transfer', defineAsyncComponent(() => import('@mobile/views/order/position/components/pricing/detail2/components/transfer/Index.vue'))],
  243. ])
  244. // // const sl = shallowRef(false) // 止损
  245. // // const sp = shallowRef(false) // 止盈
  246. // const total = computed(() => {
  247. // const { avaiableMoney = 0 } = accountStore.currentAccount
  248. // const { marketmarginalgorithm = 0, marketmarginvalue = 0, agreeunit = 0 } = quote.value ?? {}
  249. // const result = {
  250. // enableQty: 0,
  251. // deposit: 0
  252. // }
  253. // const fixed = agreeunit * marketmarginvalue
  254. // const ratio = fixed * (formData.OrderPrice ?? 0)
  255. // if (fixed && ratio) {
  256. // if (marketmarginalgorithm === 1) {
  257. // const qty = Math.trunc(avaiableMoney / ratio)
  258. // result.deposit = (formData.OrderQty ?? 0) * ratio
  259. // result.enableQty = qty > 0 ? qty : 0
  260. // }
  261. // if (marketmarginalgorithm === 2) {
  262. // const qty = Math.trunc(avaiableMoney / fixed)
  263. // result.deposit = (formData.OrderQty ?? 0) * fixed
  264. // result.enableQty = qty > 0 ? qty : 0
  265. // }
  266. // }
  267. // return result
  268. // })
  269. // 计算市价
  270. const marketPrice = computed(() => {
  271. const { ask = 0, bid = 0 } = quote.value ?? {}
  272. return formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
  273. })
  274. // 买方向持仓数量
  275. // const buyQty = computed(() => positionStore.getOrderQty(BuyOrSell.Buy, goodsCode))
  276. // 卖方向持仓数量
  277. // const sellQty = computed(() => positionStore.getOrderQty(BuyOrSell.Sell, goodsCode))
  278. // 表单验证规则
  279. const formRules: { [key: string]: FieldRule[] } = {
  280. OrderQty: [{
  281. message: t('quote.pricing.tips1'),
  282. validator: () => {
  283. return !!formData.OrderQty
  284. }
  285. }],
  286. // 限价
  287. MarketMaxSub: [{
  288. message: t('quote.pricing.tips3'),
  289. validator: (val) => {
  290. if (formData.PriceMode === PriceMode.Market) {
  291. if (val < 0) {
  292. return t('quote.pricing.tips3')
  293. }
  294. return true
  295. }
  296. return !!formData.MarketMaxSub
  297. }
  298. }],
  299. // 限价
  300. OrderPrice: [{
  301. message: t('quote.pricing.tips2'),
  302. validator: () => {
  303. if (formData.PriceMode === PriceMode.Limit) {
  304. return !!formData.OrderPrice
  305. }
  306. return true
  307. }
  308. }],
  309. // // SlPrice: [{
  310. // // message: '请输入止损价',
  311. // // validator: () => {
  312. // // if (sl.value) {
  313. // // return !!formData.SlPrice
  314. // // }
  315. // // return true
  316. // // }
  317. // // }],
  318. // // SpPrice: [{
  319. // // message: '请输入止盈价',
  320. // // validator: () => {
  321. // // if (sp.value) {
  322. // // return !!formData.SpPrice
  323. // // }
  324. // // return true
  325. // // }
  326. // // }],
  327. }
  328. const onBuyOrSellChanged = () => {
  329. if (formData.PriceMode === PriceMode.Limit) {
  330. const { ask = 0, bid = 0 } = quote.value ?? {}
  331. formData.OrderPrice = formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
  332. }
  333. }
  334. const onPriceModeChanged = () => {
  335. if (formData.PriceMode === PriceMode.Limit) {
  336. const { ask = 0, bid = 0 } = quote.value ?? {}
  337. formData.OrderPrice = formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
  338. }
  339. }
  340. const onBeforeSubmit = (buildType: BuildType) => {
  341. formData.BuildType = buildType
  342. if (buildType === BuildType.Close) {
  343. const { ask = 0, bid = 0 } = quote.value ?? {}
  344. formData.OrderPrice = formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
  345. const datas = position.value.filter(item => formData.BuyOrSell === BuyOrSell.Buy ? item.buyorsell === BuyOrSell.Sell : item.buyorsell === BuyOrSell.Buy) // 反方向持仓
  346. if (datas.length > 0) {
  347. formData.OrderQty = datas[0].enableqty
  348. }
  349. } else {
  350. formData.RelatedID = undefined
  351. }
  352. formRef.value?.submit()
  353. }
  354. // 委托下单
  355. const onSubmit = () => {
  356. const { goodsid, marketid } = quote.value ?? {}
  357. formData.GoodsID = goodsid
  358. formData.MarketID = marketid
  359. // 市价
  360. if (formData.PriceMode === PriceMode.Market) { formData.OrderPrice = marketPrice.value }
  361. // 限价
  362. // if (!sl.value) { formData.SlPrice = undefined }
  363. // if (!sp.value) { formData.SpPrice = undefined }
  364. fullloading((hideLoading) => {
  365. formSubmit().then(() => {
  366. hideLoading()
  367. dialog({ message: t('common.submitsuccess'), confirmButtonText: t('operation.confirm') }).then(() => {
  368. // 成交通知
  369. eventBus.$emit('OrderDealedNtf')
  370. })
  371. }).catch((err) => {
  372. hideLoading(err, 'fail')
  373. })
  374. })
  375. }
  376. const onRadioChange = (value: number) => {
  377. formData.OrderQty = value
  378. }
  379. onMounted(() => {
  380. formData.BuyOrSell = BuyOrSell.Buy
  381. formData.BuildType = BuildType.Open
  382. formData.PriceMode = PriceMode.Market
  383. formData.MarketMaxSub = 100.0
  384. /// 默认价格和数量
  385. const { last = 0, presettle = 0 } = quote.value ?? {}
  386. formData.OrderPrice = last || presettle
  387. formData.OrderQty = qtyStep.value
  388. /// 如果是从头寸点进来的
  389. if ( buildType === BuildType.Close) {
  390. formData.OrderQty = orderQty
  391. formData.BuyOrSell = buyOrSell
  392. formData.BuildType = buildType
  393. }
  394. })
  395. </script>
  396. <style lang="less">
  397. @import './index.less';
  398. </style>