<template>
|
<div class="order-area order-area-wrap flex flex-col flex-1 font-28">
|
<!-- 父:order-area_tabs;子1:两个按钮的 div;子2:中间图片 div(父相子绝,图片在中间) -->
|
<div class="order-area_tabs">
|
<div class="order-tabs-buttons">
|
<p class="order-tab order-tab--left font-28 flex items-center justify-center"
|
:class="currentType == 'open' ? 'order-tab--buy' : 'order-tab-buy'" @click="changeTab('open')">{{ $t('买入') }}</p>
|
<p class="order-tab order-tab--right font-28 flex items-center justify-center"
|
:class="currentType == 'close' ? 'order-tab--sell' : 'order-tab-sell'" @click="changeTab('close')">{{ $t('卖出') }}</p>
|
</div>
|
<div class="order-tabs-bg">
|
<img src="@/assets/imgs/new/contract-slash.png" alt="" class="order-tabs-bg-img" />
|
</div>
|
</div>
|
|
<!-- Limit 下拉 -->
|
<div class="order-field order-field--select mt-18 mb-18" style="position:relative;">
|
<div class="order-field-inner flex justify-between items-center w-full h-70 px-16" @click="selectBtn">
|
<div class="order-field-label">{{ title }}</div>
|
<img src="@/assets/image/public/grey-select.png" alt="select-icon" class="w-22 h-11" />
|
</div>
|
<div class="option-box" v-if="isShow">
|
<div class="font-30" v-for="item in selectData" :key="item.type" @click="selectItem(item)">{{ item.title }}</div>
|
</div>
|
</div>
|
<!-- 价格 -->
|
<div class="order-field h-70 lh-70 mb-18 flex items-center px-16">
|
<input placeholder="" class="order-input flex-1 border-none" v-model="form.price" :disabled="type / 1 === 1" />
|
<span class="order-unit">USDT</span>
|
</div>
|
<!-- 数量/总额 标题行 -->
|
<div class="order-field h-70 lh-70 mb-18 flex items-center px-16 textColor2">
|
<span>{{ title }}</span>
|
</div>
|
|
<div class="flex total-list">
|
<div class="total-div" :class="[!isTotal ? 'active-bg' : '']" @click="checkIsTotal(false)">{{ $t('数量') }}</div>
|
<div class="total-div" :class="[isTotal ? 'active-bg' : '']" @click="checkIsTotal(true)">{{ $t('总额') }}</div>
|
</div>
|
<div v-if="!isTotal" class="order-field h-70 lh-70 mb-18 flex items-center px-16">
|
<input :placeholder="$t('数量')" class="order-input flex-1 border-none textColor font-28" v-model="form.volume" @input="onInput" />
|
<span class="order-unit textColor font-28">{{ strToArr(symbolName.toLocaleUpperCase(), '/')[0] }}</span>
|
</div>
|
<div v-if="isTotal" class="order-field h-70 lh-70 mb-18 flex items-center px-16">
|
<input :placeholder="$t('总额')" class="order-input flex-1 border-none textColor font-28" v-model="form.total" @input="onInputTotal" />
|
<span class="order-unit textColor font-28">USDT</span>
|
</div>
|
<!-- 25% 50% 75% 100% -->
|
<div class="order-percent-row w-full flex justify-between items-center gap-8">
|
<span class="order-percent-btn" :class="{ 'order-percent-btn--active': percentageVal == 25 }" @click="exchangeVal(25)">25%</span>
|
<span class="order-percent-btn" :class="{ 'order-percent-btn--active': percentageVal == 50 }" @click="exchangeVal(50)">50%</span>
|
<span class="order-percent-btn" :class="{ 'order-percent-btn--active': percentageVal == 75 }" @click="exchangeVal(75)">75%</span>
|
<span class="order-percent-btn" :class="{ 'order-percent-btn--active': percentageVal == 100 }" @click="exchangeVal(100)">100%</span>
|
</div>
|
<!-- 可用 + 加号 -->
|
<div class="flex justify-between items-center mt-20 order-available-row">
|
<div class="flex flex-col font-24">
|
<p v-if="currentType === 'open'"><span class="order-available-label">{{ $t('可用') }}</span><span class="order-available-value ml-8">{{ initOpen.volume }} USDT</span></p>
|
<p v-else><span class="order-available-label">{{ $t('可卖') }}</span><span class="order-available-value ml-8">{{ initClose.volume }} {{ strToArr(symbolName.toLocaleUpperCase(), '/')[0] }}</span></p>
|
</div>
|
<div class="order-add-btn" @click="$router.push('/cryptos/exchangePage')">
|
<van-icon name="add-o" class="font-30" />
|
</div>
|
</div>
|
<!-- 买入/卖出 主按钮 -->
|
<div class="order-submit-btn w-full h-68 lh-90 flex justify-center items-center text-white text-center font-28 mt-10"
|
:class="currentType === 'open' ? 'order-submit-btn--buy' : 'order-submit-btn--sell'" @click="order()">
|
{{ currentType == 'open' ? $t('买入') : $t('卖出') }}
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import config from "@/config";
|
import { Popup, Tabs, Tab, showFailToast, showSuccessToast } from 'vant';
|
import 'vue-slider-component/theme/default.css'
|
import { _getBalance } from "@/service/user.api.js";
|
import TradeApi from "@/service/trading.js";
|
import { strToArr } from '@/utils/utis'
|
import { mapGetters } from "vuex";
|
// import PopupConfirmOrder from '@/components/popup-confirm-order'
|
export default {
|
name: "perpetualPosition",
|
components: {
|
[Popup.name]: Popup,
|
[Tabs.name]: Tabs,
|
[Tab.name]: Tab
|
},
|
props: {
|
symbol: {
|
type: String,
|
default: ''
|
},
|
symbolName: {
|
type: String,
|
default: ''
|
},
|
price: {
|
type: [Number, String],
|
default: '0.00'
|
},
|
initOpen: {
|
type: Object,
|
default() {
|
return {}
|
}
|
},
|
initClose: {
|
type: Object,
|
default() {
|
return {}
|
}
|
},
|
},
|
computed: {
|
...mapGetters('user', ['userInfo'])
|
},
|
activated() {
|
this.selectData = [
|
{ title: this.$t('市价委托'), type: '1' },
|
{ title: this.$t('限价委托'), type: '2' },
|
]
|
this.title = this.selectData[0].title
|
this.type = this.selectData[0].type
|
this.form.order_price_type = 'opponent'
|
},
|
watch: {
|
initOpen: { // 处理滚动条初始值
|
deep: true,
|
handler(val) {
|
if (val.volume / 1 > 0) {
|
this.handleInitSliderOption()
|
}
|
}
|
},
|
initClose: { // 处理滚动条初始值
|
deep: true,
|
handler(newVal, oldVal) {
|
if (newVal.volume / 1 > 0) {
|
this.handleInitSliderOption(true)
|
}
|
}
|
},
|
price(val) {
|
if (this.type === '1') { // !this.focus &&
|
this.form.price = val
|
}
|
},
|
},
|
data() {
|
return {
|
options: config.sliderOptions,
|
value: 0,
|
isShow: false,
|
title: this.$t('市价委托'),
|
selectData: [],
|
form: {
|
volume: '',
|
session_token: '',
|
symbol: '', // 币种
|
price: '',
|
total: '',
|
order_price_type: 'opponent', // 市价or限价
|
},
|
type: "1",//选中市价或限价类型
|
currentType: "open", //开仓类型
|
interval: 0.001,
|
marks: (val) => val % 25 === 0,
|
isTotal: false,
|
percentageVal: 0
|
}
|
},
|
mounted() {
|
this.selectData = [
|
{ title: this.$t('市价委托'), type: '1' },
|
{ title: this.$t('限价委托'), type: '2' },
|
]
|
this.title = this.selectData[0].title
|
this.type = this.selectData[0].type
|
this.form.order_price_type = 'opponent'
|
},
|
methods: {
|
strToArr,
|
checkIsTotal(val) {
|
this.isTotal = val
|
this.percentageVal = 0
|
this.form.total = ''
|
this.form.volume = ''
|
},
|
exchangeVal(val) {
|
if (!this.userInfo.token) {
|
this.$router.push('/login')
|
return
|
}
|
this.percentageVal = val
|
const isFull = (val === 100)
|
if (!this.isTotal) {
|
if (this.currentType == 'open') {
|
let sum = (parseFloat(this.initOpen.volume) * (val / 100) / parseFloat(this.form.price))
|
this.form.volume = isFull ? sum : (Math.floor(sum * 100000) / 100000)
|
} else {
|
let sum = parseFloat(this.initClose.volume) * (val / 100)
|
if (isFull) {
|
this.form.volume = this.initClose.volume != null ? String(this.initClose.volume) : sum
|
} else {
|
this.form.volume = Math.floor(sum * 100000) / 100000
|
}
|
}
|
} else {
|
if (this.currentType == 'open') {
|
if (isFull) {
|
this.form.total = this.initOpen.volume != null ? String(this.initOpen.volume) : (this.initOpen.volume * (val / 100))
|
} else {
|
this.form.total = this.initOpen.volume * (val / 100)
|
}
|
} else {
|
const total = this.initClose.volume * (val / 100) * parseFloat(this.form.price)
|
this.form.total = isFull ? total : (Math.floor(total * 1000) / 1000)
|
}
|
}
|
},
|
handleInitSliderOption(volume) {
|
if (!volume) { // 金额是否需要变动
|
this.form.volume = ''
|
}
|
console.log(this.initOpen.volume)
|
let vol
|
if (this.currentType === 'open') {
|
vol = this.initOpen.volume / 1
|
} else {
|
vol = this.initClose.volume / 1
|
}
|
this.options.max = Number(vol.toFixed(3))
|
if (this.options.max > 0) {
|
this.options.disabled = false
|
} else {
|
this.options.disabled = true
|
}
|
},
|
onInput() { // 数量变化
|
if (this.currentType === 'open') {
|
let maxSum = (parseFloat(this.initOpen.volume) / parseFloat(this.form.price))
|
console.log(this.initOpen.volume)
|
console.log(this.form.price)
|
console.log(maxSum)
|
if (this.form.volume * 1 > maxSum / 1) {
|
this.form.volume = maxSum
|
}
|
} else {
|
if (this.form.volume * 1 / 1 > this.options.max / 1) {
|
this.form.volume = this.options.max / 1
|
}
|
}
|
},
|
onInputTotal() { //总额变化
|
//最大额度
|
if (this.form.total > 0) {
|
if (this.currentType === 'open') {
|
console.log(this.initOpen.volume)
|
if (this.form.total * 1 > this.initOpen.volume * 1) {
|
this.form.total = this.initOpen.volume
|
}
|
} else {
|
let sum = (parseFloat(this.form.total) / parseFloat(this.form.price)).toFixed(5)
|
if (parseFloat(sum) > parseFloat(this.initClose.volume)) {
|
this.form.total = (parseFloat(this.initClose.volume) * parseFloat(this.form.price)).toFixed(5)
|
}
|
}
|
}
|
},
|
//价格类型下拉框切换
|
selectBtn() {
|
this.isShow = !this.isShow;
|
},
|
//选择价格类型
|
selectItem(item) {
|
if (item.type == 1) {
|
this.form.price = this.price
|
}
|
this.form.order_price_type = item.type === '1' ? 'opponent' : 'limit'
|
this.title = item.title;
|
this.type = item.type;
|
this.isShow = false;
|
},
|
//选择开仓类型
|
changeTab(type) { // 开仓和
|
this.percentageVal = 0
|
this.isTotal = false
|
this.form.total = ''
|
console.log('changeTab', type)
|
// TODO: 这里应该是换函数
|
// this.form.direction = type === 'open' ? 'buy' : 'sell'
|
if (this.currentType === type) {
|
return
|
}
|
this.currentType = type;
|
if (this.currentType === 'close') {
|
this.$emit('ordered', 'close') // 更新平仓初始化参数
|
} else {
|
this.$emit('ordered', 'open') // 更新平仓初始化参数
|
}
|
this.handleInitSliderOption()
|
},
|
order() {
|
if (!this.userInfo.token) {
|
this.$router.push('/login')
|
return
|
}
|
let volume = ''
|
if (this.isTotal) {
|
if (!this.form.total) {
|
showFailToast(this.$t('请输入总额'))
|
return
|
}
|
const noDecimalLimit = (this.percentageVal === 100)
|
if (this.currentType === 'open') {
|
this.form.volume = noDecimalLimit ? parseFloat(this.form.total) : parseFloat(this.form.total).toFixed(5)
|
} else {
|
this.form.volume = parseFloat(this.form.total) / parseFloat(this.form.price)
|
this.form.volume = noDecimalLimit ? this.form.volume : this.form.volume.toFixed(5)
|
}
|
volume = this.form.volume
|
} else {
|
if (!this.form.volume) {
|
showFailToast(this.$t('请输入数量'))
|
return
|
}
|
if (this.currentType === 'open') {
|
volume = (parseFloat(this.form.volume) * parseFloat(this.form.price))
|
} else {
|
volume = this.form.volume
|
}
|
}
|
this.form.symbol = this.$route.params.symbol
|
let _order = null // api
|
let emitFunc = null // 触发函数
|
|
if (this.currentType === 'open') {
|
this.form.session_token = this.initOpen.session_token
|
} else {
|
this.form.session_token = this.initClose.session_token
|
}
|
_order = this.currentType === 'open' ? TradeApi.tradeBuy : TradeApi.tradeSell
|
emitFunc = this.currentType
|
_order({
|
volume: volume,
|
session_token: this.form.session_token,
|
symbol: this.form.symbol, // 币种
|
price: this.form.price,
|
total: this.form.total,
|
order_price_type: this.form.order_price_type, // 市价or限价
|
}).then((res) => {
|
showSuccessToast(this.$t('操作成功'))
|
this.form.volume = ''
|
this.form.total = ''
|
_getBalance().then(data => { // 刷新余额
|
this.$store.commit('user/SET_USERINFO', { balance: data.money })
|
})
|
this.$emit('ordered', emitFunc)
|
}).catch(() => { // 也需要重新初始化
|
this.$emit('ordered', emitFunc)
|
})
|
},
|
}
|
|
}
|
</script>
|
|
|
|
<style lang="scss">
|
@import "@/assets/init.scss";
|
|
/* 一比一还原图片样式:白底容器 + 绿/红标签 + 浅灰输入框 + 蓝字百分比 + 绿/红主按钮 */
|
$order-input-bg: #f5f5f5;
|
$order-input-border: #dcdcdc;
|
$order-text: #333333;
|
$order-unit: #666666;
|
$order-placeholder: #999999;
|
$order-buy: #38c172;
|
$order-sell: #f4374b;
|
$order-percent: #3b82f6;
|
$order-add-border: #8a2be2;
|
|
.order-area-wrap {
|
background: #fff;
|
border-radius: 12px;
|
padding: 0 0 0 0;
|
overflow: hidden;
|
}
|
|
.order-area_tabs {
|
position: relative; /* 父相 */
|
height: 66px;
|
border-radius: 8px 8px 0 0;
|
overflow: hidden;
|
/* 子1:两个按钮的 div,flex 左右分布,中间留缝 */
|
.order-tabs-buttons {
|
display: flex;
|
justify-content: space-between;
|
align-items: stretch;
|
width: 100%;
|
height: 100%;
|
position: relative;
|
z-index: 0;
|
}
|
.order-tab {
|
flex: 0 0 calc(50% );
|
width: calc(50% );
|
font-weight: 500;
|
transition: background 0.2s, color 0.2s;
|
cursor: pointer;
|
}
|
.order-tab--left {
|
border-radius: 8px 0 0 0;
|
}
|
.order-tab--right {
|
border-radius: 0 8px 0 0;
|
}
|
.order-tab--buy {
|
background: $order-buy;
|
color: #fff;
|
}
|
.order-tab-buy {
|
background: #e8e8e8;
|
color: #000;
|
}
|
.order-tab--sell {
|
background: $order-sell;
|
color: #fff;
|
}
|
.order-tab-sell {
|
background: #e8e8e8;
|
color: #000;
|
}
|
/* 子2:中间图片 div,父相子绝,居中在两按钮中间,直接显示 img */
|
.order-tabs-bg {
|
position: absolute;
|
left: 49.3%;
|
transform: translateX(-50%);
|
width: 90px;
|
height: 130%;
|
top: -5px;
|
bottom: 0;
|
z-index: 1;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
.order-tabs-bg-img {
|
width: 100%;
|
height: 100%;
|
object-fit: contain;
|
}
|
}
|
|
.order-field {
|
background: $order-input-bg;
|
border: none;
|
border-radius: 8px;
|
color: $order-text;
|
.order-field-inner { color: $order-text; }
|
.order-field-label { color: $order-text; flex: 1; }
|
.order-input { background: transparent !important; color: $order-text; font-size: 28px; }
|
.order-unit { color: $order-unit; margin-left: 8px; font-size: 26px; }
|
input::placeholder { color: $order-placeholder; }
|
}
|
|
.order-field--select .order-field-inner {
|
min-height: 70px;
|
}
|
|
.order-percent-row {
|
margin-bottom: 4px;
|
}
|
.order-percent-btn {
|
flex: 1;
|
text-align: center;
|
padding: 2px 0;
|
background: $order-input-bg;
|
border: none;
|
border-radius: 8px;
|
color: $order-text;
|
font-size: 24px;
|
margin-right: 8px;
|
&:last-child { margin-right: 0; }
|
}
|
.order-percent-btn--active {
|
color: $order-text;
|
font-weight: 500;
|
}
|
|
.order-available-label { color: $order-placeholder; }
|
.order-available-value { color: $order-unit; }
|
.order-add-btn {
|
width: 44px;
|
height: 44px;
|
border-radius: 10px;
|
border: none;
|
background: #fff;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
color: $order-add-border;
|
}
|
|
.order-submit-btn {
|
border-radius: 10px;
|
font-weight: 700;
|
}
|
.order-submit-btn--buy { background: $order-buy; }
|
.order-submit-btn--sell { background: $order-sell; }
|
|
.bg-f3 {
|
background: $border-grey;
|
}
|
|
.options {
|
box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.3);
|
border-radius: 6px;
|
|
div {
|
font-size: 22px;
|
text-align: center;
|
width: 140px;
|
height: 60px;
|
line-height: 60px;
|
|
&.option-active {
|
background-color: $inp-b;
|
}
|
}
|
}
|
|
.right-word {
|
top: 50%;
|
transform: translateY(-50%);
|
right: 19px;
|
position: absolute;
|
border-radius: 6px;
|
}
|
|
.vue-slider {
|
height: 3px !important;
|
padding: 0 10px !important;
|
}
|
|
.vue-slider-dot {
|
width: 28px !important;
|
height: 28px !important;
|
|
}
|
|
.vue-slider-mark-label {
|
font-size: 16px;
|
margin-top: 14px !important;
|
}
|
|
.vue-slider-mark-label-active {
|
color: $text_color1 !important;
|
}
|
|
.vue-slider-dot-handle {
|
border: 3px solid $btn_main;
|
}
|
|
.vue-slider-mark {
|
width: 20px !important;
|
height: 20px !important;
|
|
&:nth-child(1) {
|
.vue-slider-mark-label {
|
transform: translateX(-14px) !important;
|
}
|
}
|
|
&:nth-child(5) {
|
.vue-slider-mark-label {
|
transform: translateX(-60px) !important;
|
}
|
}
|
}
|
|
// 开仓样式
|
.tabcon {
|
// padding-bottom: 16%;
|
position: relative;
|
height: 80px;
|
}
|
|
.tab {
|
position: absolute;
|
left: 0px;
|
right: 0px;
|
top: 0px;
|
bottom: 0px;
|
display: flex;
|
align-items: center;
|
background-color: $border-grey;
|
border-radius: 5px;
|
overflow: hidden;
|
}
|
|
.tab>* {
|
height: 100%;
|
}
|
|
.tab>img {
|
margin-left: -2px;
|
margin-right: -2px;
|
}
|
|
.tab>a {
|
flex-grow: 1;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
cursor: pointer;
|
}
|
|
/* .open / .close 已由 .order-tab--buy / .order-tab--sell 替代 */
|
|
.option-box {
|
position: absolute;
|
left: 0;
|
right: 0;
|
top: 100%;
|
margin-top: 4px;
|
width: 100%;
|
background: #fff;
|
border: none;
|
border-radius: 8px;
|
text-align: center;
|
color: $order-text;
|
z-index: 10;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
}
|
|
.option-box > div {
|
padding: 24px 0;
|
}
|
|
.option-box > div:hover {
|
background: $order-input-bg;
|
}
|
|
.num-text-color {
|
color: #4C4A54;
|
}
|
|
select {
|
appearance: none;
|
/* 去掉默认图标 */
|
-moz-appearance: none;
|
/* Firefox */
|
-webkit-appearance: none;
|
/* Safari and Chrome */
|
padding: 0 20px 0 20px;
|
background: url("../../assets/image/public/grey-select.png") no-repeat scroll right center transparent;
|
/* 自己的图*/
|
background-size: 20px 11px;
|
}
|
|
.with100 {
|
width: 100%;
|
}
|
|
.height100 {
|
height: 100%;
|
}
|
|
.targetPrice {
|
|
opacity: 1;
|
}
|
|
/* 输入框样式已统一到 .order-field */
|
|
.total-list {
|
display: flex;
|
color: $order-text;
|
font-size: 26px;
|
align-items: center;
|
justify-content: center;
|
margin-bottom: 20px !important;
|
gap: 8px;
|
|
.total-div {
|
flex: 1;
|
text-align: center;
|
padding: 8px 0 !important;
|
background: $order-input-bg;
|
border: none;
|
border-radius: 8px;
|
color: $order-unit;
|
}
|
}
|
|
.active-bg {
|
background: #fff !important;
|
border: none;
|
color: $order-text !important;
|
font-weight: 500;
|
}
|
</style>
|