<template>
|
<div :key="symbol" class="pb-108 no_touch">
|
<!-- 头部区 -->
|
|
<ContractHeader
|
:symbol="symbol"
|
:range="range"
|
:selectIndex="selectIndex"
|
:balance="userInfo.balance"
|
@tab="onTopTab"
|
@update-coin="onUpdate"
|
>
|
</ContractHeader>
|
|
<div :class="{ slide2: animated1 }" v-if="selectIndex === 1">
|
<div class="mainBackground" key="x">
|
<PerpetualOpen
|
class="pl-30 pr-30"
|
:key="keyIndex + 'a'"
|
:selectIndex="selectIndex"
|
:symbol="symbol"
|
:green-data="bids"
|
:red-data="asks"
|
:price="price"
|
:init-open="initOpen"
|
:init-close="initClose"
|
:init-futrue="initFutrue"
|
@ordered="onOrdered"
|
:currentType="currentType"
|
@changeCurrentType="changeCurrentType"
|
@changeValueBack="changeValueBack"
|
>
|
</PerpetualOpen>
|
<!-- <div class="line"></div> -->
|
<PerpetualOrder
|
class="pl-30 pr-30"
|
:key="keyIndex + 'b'"
|
:symbol="symbol"
|
:order-cur="orderCur"
|
:order-hold="orderHold"
|
:topIndex="selectIndex"
|
:futrue-hold="futrueHold"
|
:futrue-histroy="futrueHistroy"
|
@tab="onTab"
|
@recall="onRecall"
|
>
|
</PerpetualOrder>
|
</div>
|
</div>
|
<div :class="{ slide1: animated2 }" v-else>
|
<div class="mainBackground" key="y">
|
<PerpetualOpen
|
class="pl-30 pr-30"
|
:key="keyIndex + 'c'"
|
:selectIndex="selectIndex"
|
:symbol="symbol"
|
:green-data="bids"
|
:red-data="asks"
|
:price="price"
|
:init-open="initOpen"
|
:init-close="initClose"
|
:init-futrue="initFutrue"
|
@ordered="onOrdered"
|
@changeValueBack="changeValueBack"
|
>
|
</PerpetualOpen>
|
<div class="line"></div>
|
<!-- 委托/持仓-->
|
<PerpetualOrder
|
class="pl-30 pr-30"
|
:key="keyIndex + 'd'"
|
:symbol="symbol"
|
:order-cur="orderCur"
|
:order-hold="orderHold"
|
:price="price"
|
:topIndex="selectIndex"
|
:futrue-hold="futrueHold"
|
:futrue-histroy="futrueHistroy"
|
@tab="onTab"
|
@recall="onRecall"
|
>
|
</PerpetualOrder>
|
</div>
|
</div>
|
<div class="fixed w-full shadow z-10 tabBackground1">
|
<div class="flex justify-between px-30 py-10">
|
<span class="font-24 textColor2">
|
{{ symbol | _symbolName(1) }} {{ $t("k线图表") }}
|
</span>
|
<van-icon
|
class="textColor"
|
@click.stop="showCharts = !showCharts"
|
:name="showCharts ? 'arrow-down' : 'arrow-up'"
|
></van-icon>
|
</div>
|
<k-line-charts
|
:update-key="updateKey"
|
:update-data="quote"
|
:symbol="symbol"
|
v-if="symbol && showCharts"
|
:showBottom="false"
|
/>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import ContractHeader from "@/components/contract-header/index.vue";
|
import PerpetualOpen from "@/components/perpetual-open/index.vue";
|
import PerpetualOrder from "@/components/perpetual-order/index.vue";
|
|
import {
|
_getBalance,
|
_getDeepData,
|
_initOpen,
|
_initClose,
|
_orderListCur,
|
_orderListHold,
|
_futrueOrderInit,
|
_futrueOrderList,
|
} from "@/API/trade.api";
|
import { _getHomeList } from "@/API/home.api";
|
import { Popup, Swipe, SwipeItem } from "vant";
|
import { mapActions, mapGetters } from "vuex";
|
import { SET_COIN_LIST } from "@/store/const.store";
|
import { WS_URL } from "@/config";
|
import { getStorage } from "@/utils/utis";
|
import KLineCharts from "@/components/kline-charts";
|
var showLength = 7;
|
export default {
|
name: "perpetualContract",
|
components: {
|
ContractHeader,
|
PerpetualOpen,
|
PerpetualOrder,
|
[Popup.name]: Popup,
|
[Swipe.name]: Swipe,
|
[SwipeItem.name]: SwipeItem,
|
KLineCharts,
|
},
|
computed: {
|
...mapGetters("user", ["userInfo"]),
|
...mapGetters({
|
coinList: "home/coinList",
|
}),
|
},
|
watch: {
|
selectIndex(val) {
|
showLength = 7;
|
},
|
},
|
data() {
|
const initArr = [];
|
for (let i = 0; i < showLength; i++) {
|
initArr.push({
|
amount: "--",
|
price: "--",
|
});
|
}
|
return {
|
quote: {},
|
initTimer: null,
|
keyIndex: 0,
|
timeout: null,
|
timer: null, // 底部持仓等的公用定时器
|
timer1: null, // tab切换动画
|
timer2: null, // 交割合约底部持仓等的公用定时器
|
balance: 0,
|
symbol: "",
|
price: "",
|
range: "",
|
stop_price_profit: "",
|
stop_price_loss: "",
|
initOpen: {},
|
initClose: {},
|
asks: initArr, // 卖单
|
bids: initArr, // 买单
|
orderCur: [], // 当前委托
|
orderHold: [], // 持有仓位
|
futrueHold: [], // 交割持仓
|
futrueHistroy: [], // 交割历史
|
sockets: {
|
quotes: null, // 行情
|
deep: null, /// 深度
|
},
|
curTab: "", // 当前委托还是持有仓位
|
selectIndex: 1, // 当前tab
|
initFutrue: {}, /// 交割合约
|
show: false, // popup
|
animated1: false,
|
animated2: false,
|
currentType: "",
|
showCharts: false,
|
// curRequest: [], //
|
};
|
},
|
async created() {
|
if (!this.coinList.length) {
|
await this.SET_COIN_LIST();
|
}
|
_getBalance().then((data) => {
|
// 获取用户余额
|
this.$store.commit("user/SET_USERINFO", { balance: data.money });
|
// const { money } = data
|
// this.balance = money
|
});
|
},
|
methods: {
|
...mapActions("home", [SET_COIN_LIST]),
|
...mapActions("user", ["GET_UERS_KYC"]),
|
|
onUpdate(symbol) {
|
// 更新
|
this.symbol = symbol;
|
this.closeSocket();
|
this.init(symbol);
|
},
|
onRecall() {
|
// 撤单or 平仓 evt
|
this.clearTimer();
|
_orderListHold(this.symbol).then((data) => {
|
this.orderHold = data;
|
});
|
this[this.curTab](this.symbol);
|
_initOpen(this.symbol).then((data) => {
|
this.initOpen = data;
|
});
|
},
|
onTopTab(evt) {
|
// 当前tab 永续/交割
|
this.keyIndex += 1;
|
this.selectIndex = evt;
|
this.clearTimer();
|
if (this.selectIndex / 1 === 1) {
|
//this.curTab = 'fetchOrderListCur'
|
this.curTab = "fetchOrderListHold";
|
// this.animated1 = true
|
// this.timer = setTimeout(() => {
|
// this.animated1 = false
|
// clearTimeout(this.timer)
|
// }, 200)
|
this.animated2 = true;
|
this.timer1 = setTimeout(() => {
|
this.animated2 = false;
|
clearTimeout(this.timer1);
|
}, 200);
|
} else {
|
// this.animated2 = true
|
// this.timer = setTimeout(() => {
|
// this.animated2 = false
|
// clearTimeout(this.timer)
|
// }, 200)
|
this.animated1 = true;
|
this.timer1 = setTimeout(() => {
|
this.animated1 = false;
|
clearTimeout(this.timer1);
|
}, 200);
|
}
|
console.log(this.curTab);
|
this[this.curTab](this.symbol);
|
},
|
onOrdered(evt) {
|
// 下单过后的回调
|
this.clearTimer();
|
// this.clearTimeout()
|
this.initParam(this.symbol, evt); // 重新初始化
|
// TODO: 这里要做判断
|
if (this.selectIndex / 1 === 1) {
|
this[this.curTab](this.symbol); // 重新调取记录
|
console.log("this.curTab", this.curTab);
|
} else {
|
// 交割合约
|
this[this.curTab](this.symbol);
|
// this.show = true
|
console.log("curTab", evt, this.curTab);
|
}
|
//console.log('下单后更新数据')
|
},
|
onTab(evt) {
|
// 点击tab后的回调
|
console.log("evt", evt);
|
this.clearTimer();
|
// this.clearTimeout()
|
this.curTab = evt;
|
this[evt](this.symbol);
|
},
|
fetchQoutes(symbol) {
|
// 获取当前行情
|
_getHomeList(symbol).then((data) => {
|
// 获取行情
|
this.handleQoutes(data);
|
this.startQuoteSocket(); // socket
|
});
|
},
|
handleQoutes(data) {
|
if (data && data.length) {
|
const cur = data[0];
|
this.price = cur.close;
|
this.range = cur.change_ratio + "";
|
this.quote = cur;
|
this.updateKey++;
|
}
|
},
|
fetchDeepData(symbol) {
|
console.log("sd");
|
_getDeepData(symbol).then((data) => {
|
// 获取深度
|
console.log(data);
|
this.handleDeep(data);
|
this.startDeepSocket(); // socket
|
});
|
},
|
handleDeep(data) {
|
if (this.symbol !== data.symbol) {
|
return;
|
}
|
this.deepData = data;
|
const { asks, bids } = data;
|
this.asks = asks.sort((a, b) => a.price - b.price).slice(0, showLength);
|
this.bids = bids.sort((a, b) => a.price - b.price).slice(-showLength);
|
},
|
startQuoteSocket() {
|
// 行情socket
|
this.sockets.quotes = new WebSocket(`${WS_URL}/1/${this.symbol}`);
|
// socket.onopen = () => {
|
// console.log('open')
|
// socket.send('hello')
|
// }
|
this.sockets.quotes.onmessage = (evt) => {
|
const { data } = evt;
|
const { code, data: _data } = JSON.parse(data);
|
if (code / 1 === 0) {
|
this.handleQoutes(_data);
|
}
|
};
|
},
|
startDeepSocket() {
|
// 开启socket链接
|
this.closeSocket();
|
|
this.socket = new WebSocket(`${WS_URL}/3/${this.symbol}`);
|
|
this.socket.onopen = () => {
|
console.log("WebSocket connection established");
|
};
|
|
this.socket.onmessage = (evt) => {
|
const { data } = evt;
|
const { code, data: _data } = JSON.parse(data);
|
if (code / 1 === 0) {
|
this.handleDeep(_data);
|
}
|
};
|
|
this.socket.onerror = (error) => {
|
console.error("WebSocket error:", error);
|
setTimeout(this.startDeepSocket, 5000); // 5秒后尝试重新连接
|
};
|
|
this.socket.onclose = (event) => {
|
console.log("WebSocket connection closed:", event);
|
// 重新连接,可以根据需求添加重连的逻辑,这里只是简单的示例
|
};
|
},
|
|
initParam(symbol, type) {
|
// 初始化参数
|
if (type === "open" || type === "long" || type === "short" || !type) {
|
let initFunTimer = null;
|
let initFun = () => {
|
_initOpen(symbol)
|
.then((data) => {
|
this.initOpen = data;
|
clearTimeout(initFunTimer);
|
initFunTimer = null;
|
})
|
.catch((err) => {
|
initFunTimer = setTimeout(() => {
|
initFun();
|
}, 3000);
|
});
|
};
|
initFun();
|
}
|
if (type === "close" || !type) {
|
let initFunTimer = null;
|
let initFun = () => {
|
_initClose(symbol)
|
.then((data) => {
|
this.initClose = data;
|
console.log(this.initClose);
|
clearTimeout(initFunTimer);
|
initFunTimer = null;
|
})
|
.catch((err) => {
|
initFunTimer = setTimeout(() => {
|
initFun();
|
}, 3000);
|
});
|
};
|
initFunTimer = setTimeout(() => {
|
initFun();
|
}, 600);
|
}
|
if (type === "futrue" || !type) {
|
_futrueOrderInit(symbol).then((data) => {
|
this.initFutrue = data;
|
});
|
}
|
},
|
fetchOrderListCur(symbol) {
|
// 当前委托
|
//console.log('当前委托')
|
if (this.userInfo.token) {
|
_orderListCur(symbol).then((data) => {
|
this.orderCur = data;
|
});
|
this.clearTimer();
|
this.timer = setInterval(() => {
|
_orderListCur(symbol).then((data) => {
|
// if (typeof this.timer === 'string') {
|
// this.timer = null
|
// return
|
// }
|
this.orderCur = data;
|
});
|
}, 1000);
|
} else {
|
this.orderCur = [];
|
}
|
},
|
fetchOrderListHold(symbol) {
|
// 当前持仓
|
if (this.userInfo.token) {
|
_orderListHold(symbol).then((data) => {
|
// this.orderHold = data
|
this.orderHold = data.sort(this.sortData);
|
});
|
this.timer = setInterval(() => {
|
_orderListHold(symbol).then((data) => {
|
// if (typeof this.timer === 'string') {
|
// this.timer = null
|
// return
|
// }
|
// this.orderHold = data
|
this.orderHold = data.sort(this.sortData);
|
});
|
}, 1000);
|
} else {
|
this.orderHold = [];
|
}
|
},
|
fetchFutrueHoldList(symbol) {
|
// 交割持仓
|
if (this.userInfo.token) {
|
let length = 0;
|
_futrueOrderList(symbol).then((data) => {
|
// this.futrueHold = data
|
this.futrueHold = data.sort(this.sortData);
|
length = data.length;
|
});
|
this.timer = setInterval(() => {
|
_futrueOrderList(symbol).then((data) => {
|
// if (typeof this.timer === 'string') {
|
// this.timer = null
|
// return
|
// }
|
|
this.futrueHold = data.sort(this.sortData);
|
if (length != data.length) {
|
length = data.length;
|
_futrueOrderInit(symbol).then((dataList) => {
|
this.initFutrue = dataList;
|
});
|
}
|
});
|
}, 1000);
|
} else {
|
this.futrueHold = [];
|
}
|
},
|
fetchFutrueHistory(symbol) {
|
// 交割历史持仓
|
_futrueOrderList(symbol, "hisorders").then((data) => {
|
this.futrueHistroy = data;
|
// this.clearTimeout()
|
});
|
},
|
init(symbol) {
|
// 初始化页面
|
this.symbol = symbol.toLowerCase();
|
this.fetchQoutes(symbol);
|
this.fetchDeepData(symbol);
|
this.initParam(symbol); // 'open'
|
this.clearTimer();
|
//if (this.curTab === 'fetchOrderListCur') {
|
if (this.curTab === "fetchOrderListHold") {
|
//this.fetchOrderListCur(symbol)
|
this.fetchOrderListHold(symbol);
|
} else {
|
this.fetchFutrueHoldList(symbol);
|
}
|
},
|
closeSocket() {
|
this.sockets.quotes && this.sockets.quotes.close();
|
this.sockets.deep && this.sockets.deep.close();
|
this.sockets.quotes = null;
|
this.sockets.deep = null;
|
},
|
// clearTimeout(noNeed) {
|
// clearTimeout(this.timeout)
|
// if (noNeed) { // 防止timout定时器因接口返回继续触发
|
// this.timeout = ''
|
// return
|
// }
|
// this.timeout = null
|
// },
|
clearTimer() {
|
clearInterval(this.timer);
|
// if (isNeed) {
|
// this.timer = ''
|
// return
|
// }
|
this.timer = null;
|
},
|
changeCurrentType(type) {
|
this.currentType = type;
|
},
|
changeValueBack(val) {
|
if (val == 0) {
|
showLength = 7;
|
} else {
|
showLength = 13;
|
}
|
},
|
sortData(a, b) {
|
return new Date(b.open_time).getTime() - new Date(a.open_time).getTime();
|
},
|
},
|
mounted() {
|
this.GET_UERS_KYC();
|
let symbol = this.$route.params.symbol;
|
let catchSymbol = getStorage("symbol");
|
if (!symbol && catchSymbol) {
|
symbol = catchSymbol;
|
}
|
if (this.selectIndex / 1 === 2) {
|
this.curTab = "fetchFutrueHoldList";
|
} else {
|
this.curTab = "fetchOrderListHold";
|
}
|
this.symbol = symbol;
|
this.init(symbol);
|
},
|
activated() {
|
let symbol = this.$route.params.symbol;
|
let catchSymbol = getStorage("symbol");
|
if (!symbol && catchSymbol) {
|
symbol = catchSymbol;
|
}
|
if (this.selectIndex / 1 === 2) {
|
this.curTab = "fetchFutrueHoldList";
|
} else {
|
this.curTab = "fetchOrderListHold";
|
}
|
this.symbol = symbol;
|
this.init(symbol);
|
},
|
|
deactivated() {
|
this.closeSocket();
|
this.clearTimer();
|
},
|
activated() {
|
this.currentType = this.$route.query.currentType
|
? this.$route.query.currentType
|
: "long";
|
},
|
beforeDestroy() {
|
this.closeSocket();
|
// this.clearTimeout(true)
|
this.clearTimer();
|
},
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
//.list-enter-active, .list-leave-active {
|
// transition: all .5s;
|
// transform: translateY(30px)
|
//}
|
//.list-enter, .list-leave-to
|
// /* .list-leave-active for below version 2.1.8 */ {
|
// transform: translateY(0)
|
//}
|
.no_touch {
|
-webkit-user-select: none;
|
user-select: none;
|
-ms-user-select: none;
|
-moz-user-select: none;
|
padding-bottom: 150px !important;
|
}
|
|
.list-enter-active,
|
.list-leave-active {
|
will-change: transform;
|
transition: all 250ms;
|
}
|
|
.list-enter {
|
opacity: 0;
|
transform: translate3d(-100%, 0, 0);
|
}
|
|
.list-leave {
|
opacity: 0;
|
transform: translate3d(100%, 0, 0);
|
}
|
|
.rounded-view {
|
border-top-left-radius: 20px;
|
border-top-right-radius: 20px;
|
box-sizing: border-box;
|
}
|
|
.my-swipe {
|
width: 100%;
|
}
|
|
.my-swipe .van-swipe-item {
|
}
|
|
.line {
|
height: 13px;
|
|
@include themify() {
|
background: themed("tab_background");
|
}
|
}
|
|
@keyframes animate1 {
|
0% {
|
opacity: 1;
|
transform: translate3d(100%, 0, 0);
|
}
|
|
// 40% {
|
// opacity: 1;
|
// transform: translate3d(50%, 0, 0);
|
// }
|
100% {
|
opacity: 1;
|
transform: translate3d(0%, 0, 0);
|
}
|
}
|
|
@keyframes animate2 {
|
0% {
|
opacity: 1;
|
transform: translate3d(-100%, 0, 0);
|
}
|
|
// 40% {
|
// opacity: 1;
|
// transform: translate3d(50%, 0, 0);
|
// }
|
100% {
|
opacity: 1;
|
transform: translate3d(0%, 0, 0);
|
}
|
}
|
|
.slide1 {
|
animation: animate1 200ms linear;
|
}
|
|
.slide2 {
|
animation: animate2 200ms linear;
|
}
|
|
.line {
|
@include themify() {
|
background: themed("divi_line");
|
}
|
}
|
|
::v-deep .contract-header {
|
@include themify() {
|
background: themed("main_background");
|
}
|
}
|
|
.tabBackground1 {
|
@include themify() {
|
border: 1px solid themed("border_color1");
|
}
|
}
|
|
.shadow {
|
bottom: 108px;
|
box-sizing: border-box;
|
}
|
</style>
|