You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

683 lines
20 KiB

<template>
<div class="contract-page">
<!-- <div class="banner-block col-xs-8">-->
<!-- <el-carousel :interval="6000" height="450px">-->
<!-- <el-carousel-item v-for="item in pcBannerList" :key="item.id">-->
<!-- <img :src="item.imgurl" width="100%" height="100%" />-->
<!-- </el-carousel-item>-->
<!-- </el-carousel>-->
<!-- </div>-->
<div class="page-top d-flex pt-2">
<div class="kline-box flex-fill mr-2">
<div
class="coin-change d-flex align-items-center py-2 pl-4 heading justify-content-between"
>
<div class="d-flex align-items-center">
<div class="coin d-flex align-items-center">
<el-popover
placement="bottom"
ref="popover"
width="400"
trigger="click"
>
<div slot="reference">
{{ activeContract.pair_name }}
<el-button size="mini">
<i class="el-icon-arrow-down"></i>
</el-button>
</div>
<div
class="markets-pair-list"
style="max-height:300px;overflow:auto;"
>
<template v-for="parent in contractList">
<div class="px-3 text-primary" :key="parent.coin_name">
{{ parent.coin_name }}
</div>
<table class="table" :key="parent.coin_name + 1">
<thead>
<tr class="text-secondary">
<th class="w-10/24">{{ $t("contract.h5") }}</th>
<th class="w-7/24">{{ $t("contract.g3") }}</th>
<th class="w-7/24 text-right">
{{ $t("contract.h6") }}
</th>
</tr>
</thead>
<tbody>
<!-- @click="activeSymbol = item.symbol" -->
<tr
v-for="item in parent.marketInfoList"
:key="item.symbol"
:class="{ active: item.symbol == activeSymbol }"
@click="ispopover1(item.symbol)"
>
<td class="w-10/24">
{{ item.symbol }}/{{ parent.coin_name }}
</td>
<td
class="w-7/24 "
:class="item.increase < 0 ? 'decreace' : 'increace'"
>
{{item.symbol == activeSymbol ? price1 : item.price}}
</td>
<td
class="w-7/24"
:class="item.increase < 0 ? 'decreace' : 'increace'"
>
{{ item.increaseStr }}
</td>
</tr>
</tbody>
</table>
</template>
</div>
</el-popover>
</div>
<div
class="price px-3 border-right"
:class="{
decreace: activeContract.increase < 0,
increace: activeContract.increase >= 0
}"
>
<!-- <span class="current">{{ activeContract.price }}$</span> -->
<span class="current">{{price1}}$</span>
&nbsp;
<span class="zf">{{ activeContract.increaseStr }}</span>
</div>
<!-- 币种价值数据 -->
<div class="d-flex fn-12">
<div class="item px-2">
<div class="title mb-1 text-secondary">
24h {{ $t("contract.h7") }}
</div>
<div>
{{ activeContract.high }}
</div>
</div>
<div class="item px-2">
<div class="title mb-1 text-secondary">
24h {{ $t("contract.h8") }}
</div>
<div>
{{ activeContract.low }}
</div>
</div>
<div class="item px-2">
<div class="title mb-1 text-secondary">
24h {{ $t("contract.h9") }}
<!-- ({{ $t("contract.e2") }}) -->
<!-- (USDT) -->
</div>
<div>
{{ activeContract.vol }}
</div>
</div>
</div>
</div>
<!-- <div><theme-change /></div> -->
</div>
<kline
:activeCoin="{ pair_name: activeContract.pair_name }"
v-if="activeContract.pair_name"
:contract="true"
:wsUrl="wsUrl"
/>
</div>
<!-- 盘口 -->
<handicap
@input="newPriceObj = $event"
@selectprice="defaultPrice = $event"
:symbol="activeSymbol"
:wsUrl="wsUrl"
:ws="ws"
class="mr-2"
ref="handicap"
/>
<!-- 交易 -->
<exchange-store
:holdPositionList="holdPositionList"
:symbol="activeSymbol"
:defaultPrice="defaultPrice"
:newPriceObj="newPriceObj"
:contractOpen="contractOpen"
@accountInfo="accountInfo = $event"
@position="getLastHoldPosition"
@getSymbolDetail="symbolDetail=$event"
/>
</div>
<page-bottom
@position="getLastHoldPosition"
@isall="
holdPositionAll = $event;
holdPosition();
"
@setactiveItem="setactiveItem"
:accountInfo="accountInfo"
:newPriceObj="newPriceObj"
:holdPositionList="holdPositionList"
:holdPositionAll="holdPositionAll"
:symbol="activeSymbol"
:symbolDetail="symbolDetail"
/>
<div
class="modal fade"
id="openContract"
tabindex="-1"
role="dialog"
aria-labelledby="contractClose"
aria-hidden="true"
>
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="contractClose">
{{ contractAgreement.title }}
</h5>
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div v-html="contractAgreement.body"></div>
</div>
<div class="modal-footer d-flex justify-content-center">
<button type="button" class="btn btn-primary" @click="opening">
{{$t('contract.j8')}}
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// import kline from "../option/kline";
// import handicap from "./handicap.vue";
// import account from "./account.vue";
// import exchangeStore from "./exchange-store.vue";
// import pageBottom from "./page-bottom.vue";
// import Contract from "../../api/contract";
// import Socket from "@/api/server/Socket.js";
// import Home from "@/api/home";
// import bus from "@/components/bus.js";
// export default {
// components: {
// kline,
// handicap,
// account,
// exchangeStore,
// pageBottom
// },
// data() {
// return {
// contractList: [],
// activeSymbol: "",
// holdPositionAll: false,
// holdPositionList: [],
// newPriceObj: {},
// pcBannerList:[],
// accountInfo: {},
// defaultPrice: "",
// wsUrl: this.Globals.Server.Path.WS1,
// ws: new Socket(this.Globals.Server.Path.WS1),
// _time: null,
// contractOpen: false,
// contractAgreement: {},
// symbolDetail:{},
// price1:0,
// intervalId:''
// };
// },
// computed: {
// activeContract(val) {
// let contractList=this.contractList
// .map(item => item.marketInfoList)
// .flat()
// .find(item => item.symbol == this.activeSymbol) || {}
// if(val.price){
// delete contractList.price
// }
// return contractList;
// },
// isLogin() {
// return Boolean(localStorage.token);
// }
// },
// watch: {
// activeSymbol() {
// this.holdPosition();
// },
// },
// destroyed() {
// clearInterval(this._time);
// },
// created() {
// // this.openStatus();
// this.getMarketList();
// this.holdPosition();
// this.indexList();
// this._time = setInterval(() => {
// if (this.contractOpen) {
// // mounted 中有定时器跑holdPosition了
// // this.holdPosition();
// }
// }, 3000);
// this.ws.on("open", () => {
// this.swapMarketList();
// });
// // console.info(this.$refs)
// bus.$on('collapse', msg => {
// this.activeContract.price =this.activeContract.symbol=='BTC'? (msg.close).toFixed(1):(msg.close).toFixed(3);
// this.holdPositionList.map(item=>{
// if(item.symbol==this.activeContract.symbol) item.realtimePrice=this.activeContract.price
// })
// });
// this.startWatchingPrice();
// },
// //三秒更新一次数据
// mounted: function () {
// setInterval(() => {
// this.holdPosition();
// }, 2000)
// },
// methods: {
// startWatchingPrice(){
// this.intervalId = setInterval(() => {
// let newPrice = localStorage.getItem('price');
// this.price1 = newPrice;
// }, 10); // 每秒检查一次
// },
// ispopover1(item){
// this.activeSymbol=item;
// this.$refs.popover.showPopper = false;
// },
// swapMarketList() {
// let msg = "swapMarketList";
// this.ws.send({
// cmd: "sub",
// msg: msg
// });
// this.ws.on("message", res => {
// let { data, sub,cmd } = res;
// if (sub == msg) {
// // console.log(data, '11--------------------')
// if( data.symbol=='BTC' )(data.price).toFixed(1)
// this.contractList = data;
// }else if (cmd == "ping") {
// this.ws.send({
// cmd: "pong"
// });
// }
// });
// this.ws.on('close',()=>{
// this.ws= new Socket(this.Globals.Server.Path.WS1),
// console.log('链接关闭');
// this.wsOpen();
// })
// },
// wsOpen(){
// this.ws.on("open", () => {
// this.swapMarketList();
// this.$refs.handicap.linkSocket()
// });
// },
// // 获取合约市场
// getMarketList() {
// Contract.getMarketList().then(res => {
// this.contractList = res;
// // 默认值
// let firstParent = res[0];
// if (firstParent) {
// let first = firstParent.marketInfoList[0];
// if (first) this.activeSymbol = first.symbol;
// }
// });
// },
// // 获取持仓
// holdPosition() {
// if (!this.isLogin) return;
// let data = {
// symbol: (this.holdPositionAll && this.activeSymbol) || ""
// };
// Contract.holdPosition(data, { loading: false }).then(res => {
// // console.Info(res)
// res.map(item=>{
// if(item.symbol==this.activeContract.symbol) item.realtimePrice=this.activeContract.price
// })
// this.holdPositionList = res;
// });
// },
// // 获取永续合约开通状态
// openStatus() {
// if(!this.isLogin) return;
// Contract.openStatus().then(res => {
// this.contractOpen = res.open;
// if (!this.contractOpen) {
// this.contractAgreement = res.contractAgreement;
// $("#openContract").modal("show");
// }
// });
// },
// // 开通永续合约
// opening() {
// Contract.opening().then(res => {
// $("#openContract").modal("hide");
// this.contractOpen = true;
// this.$message.success(this.$t('contract.j9'));
// });
// },
// indexList() {
// Home.indexList().then((res) => {
// this.pcBannerList = res.pcBannerList
// setTimeout(() => {
// this.skroll();
// }, 100);
// }).catch((res) => {});
// },
// //获取某一个持仓订单的数据
// setactiveItem(index=0){
// this.accountInfo=this.holdPositionList[index]
// },
// // 线上最新数据有延迟,多请求两次以保拿到
// getLastHoldPosition(){
// let ti = 0,timer=null;
// timer = setInterval(()=>{
// this.holdPosition();
// ti++;
// if(ti==3){
// clearInterval(timer);
// }
// }, 1000)
// }
// }
// };
import kline from "../option/kline";
import handicap from "./handicap.vue";
import account from "./account.vue";
import exchangeStore from "./exchange-store.vue";
import pageBottom from "./page-bottom.vue";
import Contract from "../../api/contract";
import Socket from "@/api/server/Socket.js";
import Home from "@/api/home";
import bus from "@/components/bus.js";
export default {
components: {
kline,
handicap,
account,
exchangeStore,
pageBottom
},
data() {
return {
contractList: [],
activeSymbol: "",
holdPositionAll: false,
holdPositionList: [],
newPriceObj: {},
pcBannerList:[],
accountInfo: {},
defaultPrice: "",
wsUrl: this.Globals.Server.Path.WS1,
// 保证全局唯一 Socket 实例
ws: null,
_time: null,
contractOpen: false,
contractAgreement: {},
symbolDetail:{},
currentIcon: '',
imge:[],
Liste:[],
price1:0,
intervalId:'',
// 新增:保存事件函数引用,用于页面销毁时解绑
swapMarketHandler: null,
wsOpenHandler: null,
collapseHandler: null
};
},
computed: {
activeContract(val) {
let contractList = this.contractList
.map(item => item.marketInfoList)
.flat()
.find(item => item.symbol == this.activeSymbol) || {};
if(val.price){
delete contractList.price;
}
return contractList;
},
isLogin() {
return Boolean(localStorage.token);
}
},
watch: {
activeSymbol() {
this.holdPosition();
},
},
created() {
// 实例化底层的 Socket
this.ws = new Socket(this.wsUrl);
this.getMarketList();
this.holdPosition();
this.indexList();
this._time = setInterval(() => {
if (this.contractOpen) {
this.holdPosition();
}
}, 3000);
// 定义 websocket open 事件回调
this.wsOpenHandler = () => {
console.log("主页面 WS 检测到连接/重连成功,请求市场列表...");
this.swapMarketList();
};
this.ws.on("open", this.wsOpenHandler);
// 定义 bus 总线事件回调
this.collapseHandler = msg => {
this.activeContract.price = this.activeContract.symbol == 'BTC' ? (msg.close).toFixed(1) : (msg.close).toFixed(3);
this.holdPositionList.map(item => {
if (item.symbol == this.activeContract.symbol) item.realtimePrice = this.activeContract.price;
});
};
bus.$on('collapse', this.collapseHandler);
this.startWatchingPrice();
},
mounted: function () {
// setInterval(() => {
// this.holdPosition();
// }, 2000);
},
// 新增:页面销毁时,彻底释放资源!
beforeDestroy() {
clearInterval(this._time);
clearInterval(this.intervalId);
// 解绑全局 Bus
if (this.collapseHandler) {
bus.$off('collapse', this.collapseHandler);
}
// 解除 websocket 上的事件监听
if (this.ws) {
if (this.wsOpenHandler) this.ws.off("open", this.wsOpenHandler);
if (this.swapMarketHandler) this.ws.off("message", this.swapMarketHandler);
// 通知服务器取消订阅(如果需要的话,也可以直接销毁 ws)
this.ws.send({ cmd: "unsub", msg: "swapMarketList" });
this.ws.destroy();
this.ws = null;
}
},
methods: {
startWatchingPrice() {
this.intervalId = setInterval(() => {
let newPrice = localStorage.getItem('price');
this.price1 = newPrice;
}, 100);
},
ispopover1(item) {
this.activeSymbol = item;
this.getMarketList();
this.$refs.popover.showPopper = false;
},
swapMarketList() {
let msg = "swapMarketList";
// 1. 如果当前没有连接,就不发,等 open 时会自动发
if (this.ws && this.ws.checkOpen()) {
this.ws.send({ cmd: "sub", msg: msg });
}
// 2. 清除旧的监听器,防止重复绑定
if (this.swapMarketHandler) {
this.ws.off("message", this.swapMarketHandler);
}
// 3. 定义并绑定最新的数据接收函数
this.swapMarketHandler = res => {
let { data, sub, cmd } = res;
if (sub == msg) {
if (data.symbol == 'BTC') (data.price).toFixed(1);
this.contractList = data;
this.Listes();
} else if (cmd == "ping") {
this.ws.send({ cmd: "pong" });
}
};
this.ws.on("message", this.swapMarketHandler);
},
Listes() {
this.Liste = [];
if(!this.contractList || !this.contractList[0] || !this.imge[0]) return;
this.contractList[0].marketInfoList.map(item => {
this.imge[0].marketInfoList.map(items => {
if (item.symbol == items.symbol) {
this.Liste.push({
pair_name: item.pair_name,
symbol: item.symbol,
icon: items.icon
});
}
});
});
},
getMarketList() {
this.currentIcon = "";
Contract.getMarketList().then(res => {
if (this.activeSymbol == '') {
this.contractList = res;
this.imge = res;
let firstParent = res[0];
if (firstParent) {
let first = firstParent.marketInfoList[0];
if (first) this.activeSymbol = first.symbol;
this.currentIcon = res[0].marketInfoList[0].icon;
}
} else {
if(res && res[0]) {
for(let item of res[0].marketInfoList) {
if (this.activeSymbol == item.symbol) {
this.currentIcon = item.icon;
}
}
}
}
});
},
holdPosition() {
if (!this.isLogin) return;
let data = {
symbol: (this.holdPositionAll && this.activeSymbol) || ""
};
Contract.holdPosition(data, { loading: false }).then(res => {
res.map(item => {
if (item.symbol == this.activeContract.symbol) item.realtimePrice = this.activeContract.price;
});
this.holdPositionList = res;
});
},
openStatus() {
if(!this.isLogin) return;
Contract.openStatus().then(res => {
this.contractOpen = res.open;
if (!this.contractOpen) {
this.contractAgreement = res.contractAgreement;
$("#openContract").modal("show");
}
});
},
opening() {
Contract.opening().then(res => {
$("#openContract").modal("hide");
this.contractOpen = true;
this.$message.success(this.$t('contract.j9'));
});
},
indexList() {
Home.indexList().then((res) => {
this.pcBannerList = res.pcBannerList;
}).catch((res) => {});
},
setactiveItem(index=0) {
this.accountInfo = this.holdPositionList[index];
},
getLastHoldPosition() {
let ti = 0, timer = null;
timer = setInterval(() => {
this.holdPosition();
ti++;
if (ti == 3) {
clearInterval(timer);
}
}, 1000);
}
}
}
</script>
<style lang="scss" scoped>
.justify-content-between ::v-deep .dark-app .theme-switch .theme-switch-button .left, .dark-app .theme-switch .theme-switch-button .right{
color:white !important
}
.active{
background-color: #eeeeee;
}
</style>