18 changed files with 3771 additions and 9 deletions
@ -0,0 +1,461 @@ |
|||
<template> |
|||
<div class="main-chart mb5" style="height: 100%;"> |
|||
<!-- TradingView Widget BEGIN --> |
|||
<div class="tradingview-widget-container" style="height: 100%;"> |
|||
<div id="tradingview_lautin" style="height: 100%;"> |
|||
<!-- 存放图表库容器 --> |
|||
</div> |
|||
</div> |
|||
<!-- TradingView Widget END --> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Model from "@/api/custom/Model"; |
|||
import Option from "@/api/option"; |
|||
import Exchange from "@/api/exchange"; |
|||
import tvStyle from "@/assets/js/tvStyle.js"; |
|||
import { mapState } from "vuex"; |
|||
import bus from "@/components/bus.js"; |
|||
|
|||
export default { |
|||
props: ["socket", "symbol", "priceDecimals"], |
|||
|
|||
data() { |
|||
return { |
|||
model: null, // 数据模型 |
|||
interval: "D" ,// [字符串格式]和TV的resolution同步 |
|||
// interval: "60" ,// [字符串格式]和TV的resolution同步 |
|||
theme:localStorage.getItem('theme')||'dark', |
|||
intertime: '' |
|||
}; |
|||
}, |
|||
|
|||
computed: { |
|||
lang() { |
|||
let local; |
|||
// console.log(localStorage.lang) |
|||
if (localStorage.lang) { |
|||
if (localStorage.lang == "tw") { |
|||
local = "zh_TW"; |
|||
} else if (localStorage.lang == "cn") { |
|||
local = "zh"; |
|||
} else if (localStorage.lang == "kor") { |
|||
local = "ko"; |
|||
} else if (localStorage.lang == "jp") { |
|||
local = "ja"; |
|||
} else if (localStorage.lang == "de") { |
|||
local = "de_DE"; |
|||
} else if (localStorage.lang == "fra") { |
|||
local = "fr"; |
|||
} else if (localStorage.lang == "it") { |
|||
local = "it"; |
|||
} else if (localStorage.lang == "pt") { |
|||
local = "pt"; |
|||
} else if (localStorage.lang == "spa") { |
|||
local = "es"; |
|||
} else if (localStorage.lang == "en") { |
|||
local = "en"; |
|||
}else { |
|||
local = "en"; |
|||
} |
|||
} |
|||
return local; |
|||
}, |
|||
// ...mapState({ |
|||
// theme: "theme" |
|||
// }) |
|||
}, |
|||
methods: { |
|||
translateInterval2Period() { |
|||
let period; |
|||
switch (this.interval) { |
|||
case "1": |
|||
period = "1min"; |
|||
break; |
|||
case "5": |
|||
period = "5min"; |
|||
break; |
|||
case "15": |
|||
period = "15min"; |
|||
break; |
|||
case "30": |
|||
period = "30min"; |
|||
break; |
|||
case "60": |
|||
period = "60min"; |
|||
break; |
|||
case "D": |
|||
case "1D": |
|||
period = "1day"; |
|||
break; |
|||
case "W": |
|||
case "1W": |
|||
period = "1week"; |
|||
break; |
|||
case "M": |
|||
case "1M": |
|||
period = "1mon"; |
|||
break; |
|||
} |
|||
return period; |
|||
}, |
|||
translateInterval2Period1() { |
|||
let period1; |
|||
switch (this.intertime) { |
|||
case "1": |
|||
period1 = "1min"; |
|||
break; |
|||
case "5": |
|||
period1 = "5min"; |
|||
break; |
|||
case "15": |
|||
period1 = "15min"; |
|||
break; |
|||
case "30": |
|||
period1 = "30min"; |
|||
break; |
|||
case "60": |
|||
period1 = "60min"; |
|||
break; |
|||
case "D": |
|||
case "1D": |
|||
period1 = "1day"; |
|||
break; |
|||
case "W": |
|||
case "1W": |
|||
period1 = "1week"; |
|||
break; |
|||
case "M": |
|||
case "1M": |
|||
period1 = "1mon"; |
|||
break; |
|||
} |
|||
return period1; |
|||
}, |
|||
// 创建分时按钮 |
|||
createBtns() { |
|||
const model = this.model; |
|||
const widget = this.model.widget; |
|||
// widget.createButton() |
|||
// .addClass("my-theme") |
|||
// .text("主题") |
|||
// .on("click", function () { |
|||
// console.log(widget); |
|||
// widget.changeTheme("aa"); |
|||
|
|||
// }).parent().addClass('my-aa') |
|||
|
|||
// 创建分时按钮 |
|||
const resolutions = [ |
|||
// { |
|||
// title: 'Time', |
|||
// resolution: '1', |
|||
// chartType: 3 |
|||
// }, // 1,2,3几种类型 |
|||
{ |
|||
title: "1" + this.$t("exchange.min"), |
|||
resolution: "1", |
|||
chartType: 1 |
|||
}, |
|||
{ |
|||
title: "5" + this.$t("exchange.min"), |
|||
resolution: "5", |
|||
chartType: 1 |
|||
}, |
|||
{ |
|||
title: "15" + this.$t("exchange.min"), |
|||
resolution: "15", |
|||
chartType: 1 |
|||
}, |
|||
{ |
|||
title: "30" + this.$t("exchange.min"), |
|||
resolution: "30", |
|||
chartType: 1 |
|||
}, |
|||
{ |
|||
title: "1" + this.$t("exchange.hour"), |
|||
resolution: "60", |
|||
chartType: 1 |
|||
}, |
|||
{ |
|||
title: "1" + this.$t("exchange.day"), |
|||
resolution: "D", |
|||
chartType: 1 |
|||
}, |
|||
{ |
|||
title: "1" + this.$t("exchange.week"), |
|||
resolution: "W", |
|||
chartType: 1 |
|||
}, |
|||
{ |
|||
title: "1" + this.$t("exchange.month"), |
|||
resolution: "M", |
|||
chartType: 1 |
|||
} |
|||
]; |
|||
|
|||
let btns = [], // 存放所有按钮 |
|||
parents = []; |
|||
resolutions.forEach(item => { |
|||
// 生成按钮 |
|||
let $parent = widget |
|||
.createButton() |
|||
.attr("title", item.title) |
|||
.addClass("my-date") |
|||
.css({ |
|||
background: (index, value) => { |
|||
if (this.interval == item.resolution) { |
|||
return this.theme=='dark'?'#ccc':'#091722'; |
|||
} |
|||
}, |
|||
color: () => { |
|||
if (this.interval == item.resolution) { |
|||
return "#758696"; |
|||
} |
|||
} |
|||
}) |
|||
.text(item.title) |
|||
.on("click", function(e) { |
|||
const btns = this.parentNode.parentNode.querySelectorAll( |
|||
".my-date" |
|||
); |
|||
|
|||
btns.forEach(btn => { |
|||
if (btn === this) { |
|||
btn.style.cssText = `background : ${this.theme=='dark'?'#ccc':'#091722'}; |
|||
color:#758696;`; |
|||
} else btn.style.cssText = ``; |
|||
}); |
|||
|
|||
// widget.chart().setResolution(item.resolution, function onReadyCallback() {}); |
|||
model.setResolution(item.resolution, function onReadyCallback() {}); |
|||
}) |
|||
.parent() |
|||
.addClass("my-group"); |
|||
|
|||
parents.push($parent[0]); // 父节点集合 |
|||
}); |
|||
widget.chart() |
|||
.createStudy( |
|||
"Moving Average", |
|||
false, |
|||
true, |
|||
[5, "close", 0], |
|||
null, |
|||
{ |
|||
"Plot.color": "#e843da", |
|||
} |
|||
); |
|||
widget.chart() |
|||
.createStudy( |
|||
"Moving Average", |
|||
false, |
|||
true, |
|||
[10, "close", 0], |
|||
null, |
|||
{ |
|||
"Plot.color": "#53b987", |
|||
} |
|||
); |
|||
widget.chart() |
|||
.createStudy( |
|||
"Moving Average", |
|||
false, |
|||
true, |
|||
[30, "close", 0], |
|||
null, |
|||
{ |
|||
"Plot.color": "#ff231f", |
|||
} |
|||
); |
|||
|
|||
// setTimeout(function () { |
|||
// // 给my-group外层套一个DIV |
|||
// const resolution_container = document.createElement("div"); |
|||
// resolution_container.className = "resolution_container"; |
|||
// $(resolution_container).append($(parents).clone()); |
|||
// $(resolution_container).replaceAll($(parents[0])); |
|||
// // |
|||
// }, 50) |
|||
}, |
|||
|
|||
// 获取交易对基本信息 |
|||
getSymbol() { |
|||
if (this.priceDecimals) { |
|||
return Promise.resolve({ |
|||
pair_name: this.priceDecimals |
|||
}); |
|||
} else { |
|||
return Exchange.getSymbolInfo({ |
|||
symbol: this.symbol |
|||
}); |
|||
} |
|||
}, |
|||
// 解压 |
|||
unzip(b64Data) { |
|||
let u8 = window.atob(b64Data); |
|||
let jiya = window.pako.inflate(u8) |
|||
let str = this.Uint8ArrayToString(jiya); |
|||
return JSON.parse(str); |
|||
}, |
|||
Uint8ArrayToString(fileData) { |
|||
var dataString = ""; |
|||
for (var i = 0; i < fileData.length; i++) { |
|||
dataString += String.fromCharCode(fileData[i]); |
|||
} |
|||
return dataString; |
|||
}, |
|||
// 获取历史记录数据 |
|||
getHistoryCallback(onLoadCallback) { |
|||
// 取图表回传的solution |
|||
this.intertime = this.interval; |
|||
this.interval = this.model.interval; |
|||
|
|||
// 转化接口所需的period |
|||
let period1 = this.translateInterval2Period1(); |
|||
let period = this.translateInterval2Period(); |
|||
if(this.intertime!==this.model.interval){ |
|||
this.model.subscribe([ |
|||
{ |
|||
cmd: "unsub", |
|||
msg: `Kline_${this.symbol}_${period1}` |
|||
}, |
|||
{ |
|||
cmd: "sub", |
|||
msg: `Kline_${this.symbol}_${period}` |
|||
} |
|||
]); |
|||
|
|||
// 已有图表库 通过修改symbol触发图标库更新 重新执行getSymbol和getBar |
|||
this.model.setSymbol(this.symbol); |
|||
} |
|||
|
|||
Option.getStockKline({ |
|||
symbol: this.symbol, |
|||
period, |
|||
size: 500, |
|||
zip:1 |
|||
}) |
|||
.then(data => { |
|||
let list = []; |
|||
// 组装tv数据格式 |
|||
for (let item of this.unzip(data.data)) { |
|||
list.push({ |
|||
time: item.id * 1000, |
|||
open: item.open, |
|||
high: item.high, |
|||
low: item.low, |
|||
close: item.close, |
|||
volume: item.amount |
|||
}); |
|||
} |
|||
let lastTime = list[list.length - 1].time; |
|||
// console.log(lastTime,'--------') |
|||
onLoadCallback(list, lastTime); |
|||
}) |
|||
.catch(err => {}); |
|||
}, |
|||
|
|||
// 初始化图表 |
|||
initTV() { |
|||
// 先订阅数据 再创建图表库 |
|||
let period = this.translateInterval2Period(); |
|||
this.model.subscribe([ |
|||
{ |
|||
cmd: "sub", |
|||
msg: `Kline_${this.symbol}_${period}` |
|||
} |
|||
]); |
|||
// console.log(this.lang) |
|||
this.model.init( |
|||
{ |
|||
// 初始化交易对和分时 |
|||
symbol: this.symbol, |
|||
interval: this.interval, |
|||
container_id: "tradingview_lautin", // 设置容器 |
|||
height: 1000, |
|||
locale: this.lang, // 语言设置 |
|||
priceDecimals: this.priceDecimals, |
|||
disabled_features:['header_resolutions','header_symbol_search', "volume_force_overlay"], |
|||
enabled_features:['header_indicators'], |
|||
overrides: tvStyle[this.theme], |
|||
// toolbar_bg: this.theme == "dark" ? "#222e3d" : "#f1f3f6",#121212 |
|||
toolbar_bg: this.theme == "dark" ? "#121212" : "#f1f3f6", |
|||
custom_css_url: `/static/Kline/charting_library/static/css/tradingview_${ |
|||
this.theme == "dark" ? "black" : "white" |
|||
}.css`, |
|||
debug:false, |
|||
}, |
|||
this.getHistoryCallback |
|||
); |
|||
|
|||
// 创建分时和主题按钮 |
|||
this.model.widget.onChartReady(this.createBtns.bind(this)); |
|||
} |
|||
}, |
|||
|
|||
watch: { |
|||
// 渲染图表库内容 |
|||
symbol(newVal, oldVal) { |
|||
if (!oldVal) { |
|||
// 初始化订阅数据并渲染图表库 |
|||
this.initTV(); |
|||
} else { |
|||
// 切换交易对 取消旧数据 订阅新数据 |
|||
let period = this.translateInterval2Period(); |
|||
this.model.subscribe([ |
|||
{ |
|||
cmd: "unsub", |
|||
msg: `Kline_${oldVal}_${period}` |
|||
}, |
|||
{ |
|||
cmd: "sub", |
|||
msg: `Kline_${newVal}_${period}` |
|||
} |
|||
]); |
|||
|
|||
// 已有图表库 通过修改symbol触发图标库更新 重新执行getSymbol和getBar |
|||
this.model.setSymbol(newVal); |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
created() { |
|||
bus.$on('sendMsg', msg => { |
|||
// console.info(msg) |
|||
// this.model = new Model(msg, this); |
|||
this.model.conglian(msg) |
|||
let period = this.translateInterval2Period(); |
|||
this.model.subscribe([ |
|||
{ |
|||
cmd: "sub", |
|||
msg: `Kline_${this.symbol}_${period}` |
|||
} |
|||
]); |
|||
|
|||
}); |
|||
}, |
|||
|
|||
mounted() { |
|||
// console.Info(this.socket) |
|||
// 初始化模型对象 建立ws连接 |
|||
this.model = new Model(this.socket, this); |
|||
|
|||
// 刷新页面时订阅数据并创建图表 |
|||
if (this.symbol) this.initTV(); // 必须在mounted之后 因为渲染tv时需要查找容器 |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
#tradingview_lautin { |
|||
height: 550px; |
|||
} |
|||
|
|||
// 分时按钮 |
|||
#tradingview_878d2 .my-group { |
|||
cursor: pointer !important; |
|||
border: 1px solid red !important; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,390 @@ |
|||
<template> |
|||
<div class="col-md-3" style="background-color: #121212;padding-left: 0px;padding-right: 0px;margin-right: 8px;"> |
|||
<div class="mb15"><!-- order-book --> |
|||
<div class="d-flex"> |
|||
<div class="heading" :class="tabcolor == 1 ?'textcolor':''" @click="tabcolor = 1">{{ $t("exchange.order-book") }}</div> |
|||
<div class="heading" :class="tabcolor == 2 ?'textcolor':''" @click="tabcolor = 2">{{ $t("exchange.recent-trades") }}</div> |
|||
</div> |
|||
<table class="table" v-if="tabcolor == 1"> |
|||
<thead> |
|||
<tr style="display:block;"> |
|||
<th class="w-33">{{ $t("exchange.price") }}({{pair.to}})</th> |
|||
<th class="w-33 text-right">{{ $t("exchange.amount") }} |
|||
<!-- ({{pair.from}}) --> |
|||
({{pair.to}}) |
|||
</th> |
|||
<th class="w-33 text-right">{{ $t("exchange.total") }}({{pair.from}})</th> |
|||
</tr> |
|||
</thead> |
|||
|
|||
<tbody class="sell-orders" v-if="symbol!='gitpusdt'"> |
|||
<div class="order-item" v-for="item in sellList" :key="item.id"> |
|||
<el-progress class="progress" type="line" :stroke-width="39" :percentage="getValue(item.amount)" :show-text="false" color="rgba(216,43,43,0.15)"> |
|||
</el-progress> |
|||
<!-- 卖单 使用弹性盒子倒序排列 --> |
|||
<tr style="display:block;" class="tb-cells" @click.stop="handleOrder(item.price)"> |
|||
<td class="w-33 red" title="pick this price to sell">{{ item.price|omitTo(priceDecimals) }}</td> |
|||
<td class="w-33 text-right">{{ item.amount|omitTo(qtyDecimals) }}</td> |
|||
<td class="w-33 text-right">{{ item.price|multiple(item.amount, priceDecimals) }}</td> |
|||
</tr> |
|||
</div> |
|||
</tbody> |
|||
|
|||
<tbody class="ob-heading"> |
|||
<tr style="display:block;" v-if="newTrade"> |
|||
<td class="w-33"> |
|||
<span>{{ $t("exchange.last-price") }}</span> |
|||
<i :class="newTrade.changeRate > 0 ? 'tri-inc' : 'tri-dec'"></i> |
|||
<b :class="newTrade.changeRate > 0 ? 'increace' : 'decreace'">{{ newTrade.price}}</b> |
|||
</td> |
|||
<td class="w-33 text-right"> |
|||
<span v-if="langs=='cn'">CNY</span> |
|||
<span v-else>$</span> |
|||
<!-- ≈ {{omitTo(newTrade.price*price_cny)}} --> |
|||
<span v-if="langs=='cn'">≈ {{newTrade.price*priceCny|omitTo(2)}}</span> |
|||
<span v-else>≈ {{newTrade.price|omitTo(2)}}</span> |
|||
</td> |
|||
<td class="w-33 text-right" :class="changeRate.startsWith('+') ? 'increace' : 'decreace'"> |
|||
<span>{{ $t("exchange.change") }}</span> |
|||
{{ changeRate }} |
|||
</td> |
|||
</tr> |
|||
<tr style="display:block;" v-else> |
|||
<td class="w-33"><span>{{ $t("exchange.last-price") }}</span> -</td> |
|||
<td class="w-33 text-right"> |
|||
<span>CNY</span> - |
|||
</td> |
|||
<td class="w-33 text-right"><span>{{ $t("exchange.change") }}</span> -</td> |
|||
</tr> |
|||
</tbody> |
|||
|
|||
<tbody class="buy-orders" :style="[{'height':symbol!='gitpusdt'?'':'470px!important'}]"> |
|||
<!-- 买单 反序数值 --> |
|||
<div class="order-item" v-for="item in reversed" :key="item.id"> |
|||
<el-progress class="progress" type="line" :stroke-width="39" :percentage="getValue(item.amount)" :show-text="false" color="rgba(37, 188, 103, 0.15)"> |
|||
</el-progress> |
|||
<!-- 卖单 使用弹性盒子倒序排列 --> |
|||
<tr style="display:block;" class="tb-cells" @click.stop="handleOrder(item.price)"> |
|||
<td class="w-33 green" title="pick this price to buy">{{ item.price|omitTo(priceDecimals) }}</td> |
|||
<td class="w-33 text-right">{{ item.amount|omitTo(qtyDecimals) }}</td> |
|||
<td class="w-33 text-right">{{ item.price|multiple(item.amount, priceDecimals) }}</td> |
|||
</tr> |
|||
</div> |
|||
|
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<div class="market-history" style="border: 0px;" v-if="tabcolor == 2"> |
|||
<ul class="nav nav-pills" role="tablist" v-if="0"> |
|||
<li class="nav-item"> |
|||
<a class="nav-link active" data-toggle="pill" href="#recent-trades" role="tab" aria-selected="true"> |
|||
{{ $t("exchange.recent-trades") }} |
|||
</a> |
|||
</li> |
|||
<!-- <li class="nav-item"> |
|||
<a class="nav-link" data-toggle="pill" href="#market-depth" role="tab" aria-selected="false">Market |
|||
Depth</a> |
|||
</li> --> |
|||
</ul> |
|||
<div class="tab-content"> |
|||
<div class="tab-pane fade show active" id="recent-trades" role="tabpanel"> |
|||
<table class="table trade-list"> |
|||
<thead> |
|||
<tr> |
|||
<th style="display:block;width:100%;"> |
|||
<div class="w-33"> |
|||
{{ $t("exchange.price") }}({{pair.to}}) |
|||
</div> |
|||
<div class="w-33 text-right"> |
|||
{{ $t("exchange.amount") }}({{pair.from}}) |
|||
</div> |
|||
<div class="w-33 text-right"> |
|||
{{ $t("exchange.time") }} |
|||
</div> |
|||
</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
|
|||
<tr v-for="(item, index) in tradeList" :key="index"> |
|||
<td style="display:block;width:100%"> |
|||
<div class="w-33" :style="{color : item.color}">{{ item.price }}</div> |
|||
<div class="w-33 text-right">{{ item.amount }}</div> |
|||
<div class="w-33 text-right">{{ item.time }}</div> |
|||
</td> |
|||
</tr> |
|||
|
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<!-- <div class="tab-pane fade" id="market-depth" role="tabpanel"> |
|||
<div class="depth-chart-container"> |
|||
<div class="depth-chart-inner"> |
|||
<div id="lightDepthChart"></div> |
|||
</div> |
|||
</div> |
|||
</div> --> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
|
|||
import Market from "@/api/market"; |
|||
|
|||
export default { |
|||
|
|||
// props : ["priceDecimals", "qtyDecimals"], |
|||
props:[ |
|||
'priceCny' |
|||
], |
|||
data() { |
|||
return this.$parent; |
|||
}, |
|||
|
|||
computed: { |
|||
langs(){ |
|||
return localStorage.getItem('lang')||'en' |
|||
}, |
|||
reversed() { |
|||
// 按给定值 倒序排列 |
|||
if (this.buyList && this.buyList.length) return this.buyList; |
|||
else return []; |
|||
|
|||
}, |
|||
|
|||
changeRate() { |
|||
|
|||
// 开盘价为0时 则返回0值 |
|||
if (!this.marketInfo || !this.marketInfo.open) return `+0.00%`; |
|||
|
|||
let val = Math.division( |
|||
Math.subtr(this.newTrade.price, this.marketInfo.open), |
|||
this.marketInfo.open); |
|||
|
|||
// 补充+号 |
|||
let sign = val >= 0 ? '+' : ''; |
|||
let percentage = `${sign}${Math.multiple(val, 100, 2)}%`; |
|||
|
|||
return percentage; |
|||
|
|||
}, |
|||
|
|||
tradeList() { |
|||
|
|||
let count = 0; |
|||
|
|||
// 处理第1~(n-1)条记录,第一条永远显示绿色 |
|||
if (this.trade && this.trade.length) { |
|||
|
|||
return this.trade.sort((m, n) => { |
|||
|
|||
let dm = Number(m.price), |
|||
dn = Number(n.price); |
|||
|
|||
if (dn >= dm) { |
|||
n.color = "#53b987"; |
|||
n.changeRate = 1; |
|||
} else { |
|||
n.color = "#ff231f"; |
|||
n.changeRate = -1; |
|||
} |
|||
|
|||
n.price = Math.omitTo(n.price, this.priceDecimals); |
|||
n.amount = Math.omitTo(n.amount, this.qtyDecimals); |
|||
n.time = Date.parseTime(n.ts, false, "{h}:{i}:{s}"); |
|||
|
|||
// 处理最后一个值 |
|||
if (++count == this.trade.length - 1) { |
|||
m.color = "#53b987"; |
|||
m.changeRate = 1; |
|||
m.price = Math.omitTo(m.price, this.priceDecimals); |
|||
m.amount = Math.omitTo(m.amount, this.qtyDecimals); |
|||
m.time = Date.parseTime(m.ts, false, "{h}:{i}:{s}"); |
|||
} |
|||
}); |
|||
} else { |
|||
return this.trade; |
|||
} |
|||
} |
|||
|
|||
}, |
|||
|
|||
methods: { |
|||
|
|||
// 计算深度 当前数量 / 买卖最大值 |
|||
getValue(amount) { |
|||
const arr = this.buyList.concat(this.sellList).map(item => item.amount); |
|||
let max = Math.max(...arr); |
|||
return Math.division(amount, max, 2) * 100; |
|||
}, |
|||
|
|||
// 将委托价带入交易控制面板中 |
|||
handleOrder(price, ordertype) { |
|||
// 修改父组件的order |
|||
if (this.passOrderPrice) { |
|||
this.buyorder.entrust_price = Math.omitTo(price, this.priceDecimals); |
|||
this.sellorder.entrust_price = Math.omitTo(price, this.priceDecimals); |
|||
} |
|||
}, |
|||
|
|||
// 测试方法 用于progress传入值 |
|||
format(item) { |
|||
return function (percentage) { |
|||
return `<span>${Math.omitTo(item.price, this.priceDecimals)}</span> |
|||
<span>${Math.omitTo(item.qty, this.qtyDecimals)}</span> |
|||
<span>${Math.multiple(item.price, item.qty, this.priceDecimals)}</span>`; |
|||
} |
|||
}, |
|||
|
|||
bookTrades() { |
|||
Market.getstockMarketInfo(this.symbol).then(response => { |
|||
// |
|||
this.sellList = response.sellList.sort((a, b) => b.price - a.price); |
|||
this.buyList = response.buyList; |
|||
this.trade = response.tradeList; |
|||
|
|||
}).catch(err => { |
|||
|
|||
}); |
|||
} |
|||
|
|||
|
|||
}, |
|||
|
|||
mounted() { |
|||
// 在mounted中重置初始化的[]值,如果在created之前的话 []会覆盖ajax查询的值 |
|||
this.bookTrades(); |
|||
}, |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.w-33 { |
|||
width: 32% !important; |
|||
display: inline-block !important; |
|||
vertical-align: text-top !important; |
|||
box-sizing: border-box !important; |
|||
} |
|||
|
|||
.buy-orders { |
|||
|
|||
height: 235px !important; |
|||
overflow: hidden !important; |
|||
|
|||
td { |
|||
@include ff(OpenSans-Regular); |
|||
} |
|||
|
|||
} |
|||
|
|||
.sell-orders { |
|||
|
|||
@extend .buy-orders; |
|||
|
|||
@include flexible(column-reverse, flex-start, flex-start, wrap); |
|||
|
|||
// flex-shrink: 0; // 不压缩 |
|||
display: flex !important; |
|||
overflow-x: hidden; // 重要 换行后 隐藏右侧内容 |
|||
|
|||
} |
|||
|
|||
.order-item { |
|||
|
|||
position: relative; |
|||
z-index: 1; |
|||
width: 100%; |
|||
height: 39px; |
|||
|
|||
.progress { |
|||
height: 100%; |
|||
border-radius: 0; |
|||
background: none; |
|||
} |
|||
|
|||
.tb-cells { |
|||
position: absolute; |
|||
left: 0; |
|||
top: 0; |
|||
height: 100%; |
|||
z-index: 100; |
|||
cursor: pointer; |
|||
|
|||
&:hover { |
|||
background: none; |
|||
|
|||
} |
|||
} |
|||
|
|||
// 给外边框添加不同的底色 |
|||
&:nth-child(even) .progress .el-progress-bar__outer { |
|||
background: #F8F8FF; |
|||
// background: #f6f8f9; |
|||
|
|||
} |
|||
|
|||
&:nth-child(odd) .progress .el-progress-bar__outer { |
|||
background: #ffffff; |
|||
} |
|||
} |
|||
|
|||
.el-progress { |
|||
|
|||
.el-progress-bar { |
|||
|
|||
/**重构progress的背景色 */ |
|||
.el-progress-bar__outer { |
|||
|
|||
// 去除外表框的圆角 |
|||
border-radius: 0 !important; |
|||
// background: #fff; |
|||
|
|||
.el-progress-bar__inner { |
|||
|
|||
border-radius: 0 !important; |
|||
|
|||
right: 0; // 将进度容器由左对齐变成右对齐 |
|||
left: auto; |
|||
|
|||
&::after {} |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
.trade-list { |
|||
tbody { |
|||
// height: 279px; |
|||
height: 505px; |
|||
|
|||
tr { |
|||
|
|||
// @include flexible(); |
|||
// td:nth-last-child { |
|||
// flex-grow: 1; |
|||
// } |
|||
td { |
|||
cursor: text !important; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
#darkDepthChart, |
|||
#lightDepthChart { |
|||
height: 305px; |
|||
} |
|||
</style> |
|||
<style lang="scss" scoped> |
|||
.ob-heading { |
|||
border: 1px solid #333; |
|||
box-shadow: 0 0 5px #555; |
|||
} |
|||
.textcolor{ |
|||
color: #007bff; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,618 @@ |
|||
<template> |
|||
<div class="col-md-8"> |
|||
|
|||
<!-- 生成K线图 --> |
|||
<k-line :symbol="symbol" :marketId="marketId" :priceDecimals="priceDecimals"></k-line> |
|||
|
|||
<!-- 交易处理 --> |
|||
<div class="market-trade"> |
|||
<ul class="nav nav-pills"> |
|||
<li class="nav-item"> |
|||
<a href :class="[`nav-link`, {active:!isCondition&&!isMarket}]" @click.prevent="isCondition=false;isMarket=false;">Limit</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a href :class="[`nav-link`, {active:!isCondition&&isMarket}]" @click.prevent="isCondition=false;isMarket=true;">Market</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a href :class="[`nav-link`, {active:isCondition&&!isMarket}]" @click.prevent="isCondition=true;isMarket=false;">Stop Limit</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a href :class="[`nav-link`, {active:isCondition&&isMarket}]" @click.prevent="isCondition=true;isMarket=true;">Stop Market</a> |
|||
</li> |
|||
</ul> |
|||
<div class="tab-content"> |
|||
<div class="tab-pane fade show active"> |
|||
<div class="d-flex justify-content-between"> |
|||
<!------------- Buy Order --------------> |
|||
<div class="market-trade-buy"> |
|||
|
|||
<!-- Trigger Price --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.trigger"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.buy[0] }} |
|||
</span> |
|||
<div class="input-group" v-if="isCondition" slot="reference"> |
|||
<input type="number" v-model="buyorder.trigger_price" class="form-control" placeholder="请输入触发价格"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.from}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
|
|||
<template v-if="isMarket"> |
|||
<div class="input-group"> |
|||
<!-- Market Price --> |
|||
<input type="text" class="form-control" disabled placeholder="在最佳市场价格成交"> |
|||
</div> |
|||
</template> |
|||
<template v-else> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.limitPrice"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.buy[1] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<!-- Entrust Price --> |
|||
<input type="number" v-model="buyorder.entrust_price" class="form-control" placeholder="请输入价格"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.from}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
|
|||
<!-- Amount/Total --> |
|||
<template v-if="isMarket"> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.marketTotal"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.buy[3] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<input type="number" v-model="buyTotal" class="form-control" min=0 placeholder="请输入总值"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.to}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
<template v-else> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.limitAmount"> |
|||
<!-- 提示框的内容 --> |
|||
<span class="content"> |
|||
<!-- 图标 --> |
|||
<i class="el-icon-warning-outline"></i> |
|||
<!-- 提示 --> |
|||
{{ msgList.buy[2] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<input type="number" v-model="buyorder.amount" class="form-control" min=0 placeholder="请输入数量"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.to}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
|
|||
<!-- Percent --> |
|||
<ul class="market-trade-list"> |
|||
<li v-for="(item,index) in percentage" :key="index" :class="{buyPercentActive:index == buyPercentIndex}" @click="renderBuyAmount(item.value, index)"> |
|||
<a href="javascript:void 0">{{ item.label }}</a> |
|||
</li> |
|||
</ul> |
|||
|
|||
<!-- Total --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.orderTotal"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.buy[5] }} |
|||
</span> |
|||
<p slot="reference"> |
|||
{{$t('exchange.order-total')}}: |
|||
<span> <b>{{buyTotal}}</b> {{pair.from}} </span> |
|||
</p> |
|||
</el-popover> |
|||
|
|||
<!-- Available --> |
|||
<p>{{$t('otc.j3')}}: |
|||
<span> {{toBalance}} {{pair.to}}</span><br /> |
|||
<span> {{fromBalance}} {{pair.from}}</span> |
|||
</p> |
|||
<button class="btn buy" @click="handleBuyOrder">Buy {{pair.to}}</button> |
|||
</div> |
|||
|
|||
<!------------ Sell Order -----------> |
|||
<div class="market-trade-sell"> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.sell.trigger"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.sell[0] }} |
|||
</span> |
|||
<!-- Trigger Price --> |
|||
<div class="input-group" v-if="isCondition" slot="reference"> |
|||
<input type="number" v-model="sellorder.trigger_price" class="form-control" min=0 placeholder="请输入触发价格"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.from}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
|
|||
<template v-if="isMarket"> |
|||
<div class="input-group"> |
|||
<!-- Market Price --> |
|||
<input type="text" class="form-control" disabled placeholder="在最佳市场价格成交"> |
|||
</div> |
|||
</template> |
|||
<template v-else> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.sell.limitPrice"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.sell[1] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<!-- Entrust Price --> |
|||
<input type="number" v-model="sellorder.entrust_price" class="form-control" min=0 placeholder="请输入价格"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.from}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
|
|||
<!-- Amount --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.sell.marketAmount"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.sell[4] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<input type="number" v-model="sellorder.amount" class="form-control" min=0 placeholder="请输入数量"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.to}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
|
|||
<ul class="market-trade-list"> |
|||
<li v-for="(item,index) in percentage" :key="index" :class="{sellPercentActive:index == sellPercentIndex}" @click="renderSellAmount(item.value, index)"> |
|||
<a href="javascript:void 0">{{ item.label }}</a> |
|||
</li> |
|||
</ul> |
|||
|
|||
<!-- Total --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.sell.orderTotal"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.sell[5] }} |
|||
</span> |
|||
<p slot="reference"> |
|||
{{$t('exchange.order-total')}}: |
|||
<span> |
|||
<b>{{sellTotal}}</b> {{pair.from}} </span> |
|||
</p> |
|||
</el-popover> |
|||
|
|||
<p>{{$t('otc.j3')}}: |
|||
<span> {{toBalance}} {{pair.to}} </span><br /> |
|||
<span> {{fromBalance}} {{pair.from}}</span> |
|||
</p> |
|||
<button class="btn sell" @click="handleSellOrder">Sell {{pair.to}}</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
|
|||
import Exchange from "@/api/exchange"; |
|||
|
|||
export default { |
|||
|
|||
props: ["pair", "buyorder", "marketId", "sellorder", "fromBalance", "toBalance", "symbol", "newTrade", "minQty", "minTotal", "priceDecimals", "qtyDecimals"], |
|||
data() { |
|||
return { |
|||
isMarket: false, |
|||
isCondition: false, |
|||
userBanlance: null, |
|||
|
|||
cacheTotal: 0, // 存放市价单输入的总值 |
|||
cachePrice: null, // 存放最新价格 |
|||
|
|||
// 气泡弹框内容和触发 |
|||
visibles: { |
|||
"buy": { |
|||
limitPrice: false, // 限制价格 |
|||
limitAmount: false, // 限制数量 |
|||
marketTotal: false, // 限制总额 |
|||
marketAmount: false, // 市价数量 |
|||
trigger: false, // 触发价格 |
|||
orderTotal: false, // 订单总额 |
|||
}, |
|||
"sell": { |
|||
limitPrice: false, |
|||
limitAmount: false, |
|||
marketTotal: false, |
|||
marketAmount: false, |
|||
trigger: false, |
|||
orderTotal: false, |
|||
}, |
|||
}, |
|||
msgList: { |
|||
"buy": Array(6).fill(''), // 5个空字符串的数组 |
|||
"sell": Array(6).fill('') |
|||
}, |
|||
// 百分比集合 |
|||
percentage: [{ |
|||
label: "25%", |
|||
value: 0.25 |
|||
}, |
|||
{ |
|||
label: "50%", |
|||
value: 0.5 |
|||
}, |
|||
{ |
|||
label: "75%", |
|||
value: 0.75 |
|||
}, |
|||
{ |
|||
label: "100%", |
|||
value: 1 |
|||
}, |
|||
], |
|||
// |
|||
buyPercentIndex: null, |
|||
sellPercentIndex: null, |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
|
|||
// 获取type类型值 |
|||
type() { |
|||
if (!this.isCondition) { |
|||
return this.isMarket ? 2 : 1; |
|||
} else { |
|||
return this.isMarket ? 4 : 3; |
|||
} |
|||
}, |
|||
|
|||
theme() { |
|||
return localStorage.theme ? localStorage.theme : "light"; |
|||
}, |
|||
|
|||
buyTotal: { |
|||
get() { |
|||
if (!this.isMarket) { |
|||
return Math.multiple(this.buyorder.entrust_price, this.buyorder.amount); |
|||
} else { |
|||
return this.cacheTotal; |
|||
} |
|||
}, |
|||
set(val) { |
|||
// 根据总值 计算数量 |
|||
if (!this.isMarket) { |
|||
this.buyorder.amount = Math.division(val, this.buyorder.entrust_price); |
|||
} else { // 市价单 缓存总值 |
|||
this.cacheTotal = val; |
|||
} |
|||
} |
|||
}, |
|||
|
|||
sellTotal: { |
|||
get() { |
|||
return Math.multiple(this.sellorder.entrust_price, this.sellorder.amount); |
|||
} |
|||
}, |
|||
|
|||
// 当前语言 |
|||
lang() { |
|||
let browser_Lang = navigator.language.includes('zh') ? 'zh' : 'en'; |
|||
return localStorage.lang || browser_Lang; |
|||
}, |
|||
|
|||
}, |
|||
|
|||
watch: { |
|||
custom(newVal) { |
|||
// console.log(newVal) |
|||
}, |
|||
|
|||
type() { |
|||
// 切换订单类型时 清空价格和数量 |
|||
// 数量改变时 触发percentage更新 |
|||
this.reset(); |
|||
}, |
|||
|
|||
// 有新交易时触发 给限价单设定初始价格 |
|||
newTrade(newVal, oldVal) { |
|||
if (!this.isMarket) { |
|||
|
|||
// 新交易对有trade数据 |
|||
if (!oldVal && newVal) { // 没值到初始化值 |
|||
// 修改order里面price的值 |
|||
this.cachePrice = newVal.price; |
|||
this.reset(); |
|||
} |
|||
|
|||
// 切换交易对时 清空了数据 |
|||
if (oldVal && !newVal) { // 有值到没值的过程 |
|||
this.cachePrice = null; |
|||
this.reset(); |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
methods: { |
|||
|
|||
renderBuyAmount(val, index) { |
|||
if (!this.buyorder.entrust_price) { |
|||
this.visibles.buy.limitPrice = true; |
|||
this.msgList.buy[1] = this.$t('nav.tips'); |
|||
this.clearAll(); |
|||
return; |
|||
} |
|||
this.buyPercentIndex = index; |
|||
this.buyTotal = Math.multiple(this.fromBalance, val); |
|||
}, |
|||
|
|||
renderSellAmount(val, index) { |
|||
if (!this.sellorder.entrust_price) { |
|||
this.visibles.sell.limitPrice = true; |
|||
this.msgList.sell[1] = this.$t('nav.tips'); |
|||
this.clearAll(); |
|||
return; |
|||
} |
|||
this.sellPercentIndex = index; |
|||
this.sellorder.amount = Math.multiple(this.toBalance, val); |
|||
}, |
|||
|
|||
handleBuyOrder() { |
|||
|
|||
// 执行前端的有效性验证 |
|||
if (!this.chkValidate(this.buyorder, this.buyTotal, "buy")) return; |
|||
|
|||
const baseArgs = { |
|||
symbol: this.symbol, |
|||
type: this.type, |
|||
}; |
|||
|
|||
// 区分限价、市价和条件委托单 |
|||
// 1、限价单买入时 需要的参数:价格、数量、 |
|||
// 2、市价单买入时 需要的参数:总值 |
|||
// 3、条件单买入时 需要的参数:触发价、价格数量或者总值 |
|||
// 4、卖出时 需要价格和数量 |
|||
Exchange.storeEntrust(Object.assign(this.buyorder, { |
|||
total: this.buyTotal, |
|||
}, baseArgs)).then(data => { |
|||
// 触发父组件的方法 更新余额和订单 |
|||
this.$emit('update'); |
|||
// 清空表单 |
|||
this.reset(); |
|||
}).catch(err => { |
|||
|
|||
}); |
|||
}, |
|||
|
|||
handleSellOrder() { |
|||
// 执行前端的有效性验证 |
|||
if (!this.chkValidate(this.sellorder, this.sellTotal, "sell")) return; |
|||
|
|||
const baseArgs = { |
|||
symbol: this.symbol, |
|||
type: this.type, |
|||
}; |
|||
Exchange.storeEntrust(Object.assign(this.sellorder, { |
|||
total: this.sellTotal |
|||
}, baseArgs)).then(data => { |
|||
// 触发父组件的方法 更新余额和订单 |
|||
this.$emit('update'); |
|||
// 清空表单 |
|||
this.reset(); |
|||
}).catch(err => { |
|||
|
|||
}); |
|||
}, |
|||
|
|||
reset() { |
|||
|
|||
// 如果没有newTrade时 则没有缓存价格 重置为最小值0 |
|||
// 由于输入框去除了精度空值 这里还需要手动设置精度值 |
|||
let price = this.cachePrice || 0; |
|||
|
|||
this.buyorder.entrust_price = Math.omitTo(price, this.priceDecimals); |
|||
this.buyorder.trigger_price = Math.omitTo(price, this.priceDecimals); |
|||
|
|||
this.sellorder.entrust_price = Math.omitTo(price, this.priceDecimals); |
|||
this.sellorder.trigger_price = Math.omitTo(price, this.priceDecimals); |
|||
|
|||
// 清空数量 |
|||
this.buyorder.amount = 0; |
|||
this.sellorder.amount = 0; |
|||
|
|||
// total赋值会触发set方法 |
|||
// this.buyTotal = 0; |
|||
// this.cacheTotal = 0; |
|||
|
|||
// 去除百分比样式 |
|||
this.buyPercentIndex = -1; |
|||
this.sellPercentIndex = -1; |
|||
}, |
|||
|
|||
clearAll() { |
|||
// 5s后统一清除提示框 |
|||
setTimeout(function () { |
|||
Object.keys(this.visibles.buy).forEach(key => this.visibles.buy[key] = false); |
|||
Object.keys(this.visibles.sell).forEach(key => this.visibles.sell[key] = false); |
|||
}.bind(this), 5000); |
|||
}, |
|||
|
|||
empty(val) { |
|||
let ret; |
|||
switch (typeof val) { |
|||
case "number": |
|||
ret = val == 0; |
|||
break; |
|||
case "string": |
|||
ret = val == "0" || /^\s?$/.test(val); |
|||
break; |
|||
case "boolean": |
|||
ret = val; |
|||
break; |
|||
default: |
|||
ret = Boolean(val); |
|||
break; |
|||
} |
|||
return ret; |
|||
}, |
|||
|
|||
chkValidate(order, total, orderType) { |
|||
let flag = true; |
|||
|
|||
switch (this.type) { |
|||
|
|||
case 1: // limit |
|||
if (this.empty(order.entrust_price)) { |
|||
flag = false; |
|||
this.visibles[orderType].limitPrice = true; |
|||
this.msgList[orderType][1] = this.$t('nav.a1'); |
|||
} else if (this.empty(order.amount)) { |
|||
flag = false; |
|||
this.visibles[orderType].limitAmount = true; |
|||
this.msgList[orderType][2] = this.$t('nav.a2'); |
|||
} else { |
|||
if (order.amount < this.minQty) { |
|||
flag = false; |
|||
this.visibles[orderType].limitAmount = true; |
|||
this.msgList[orderType][2] = this.$t('nav.a3')+`${this.minQty}`; |
|||
} |
|||
|
|||
if (total < this.minTotal) { |
|||
flag = false; |
|||
this.visibles[orderType].orderTotal = true; |
|||
this.msgList[orderType][5] = this.$t('nav.b8')+`${this.minTotal}`; |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case 2: // market |
|||
if (order.direction == "buy") { // 买 |
|||
if (this.empty(total)) { |
|||
flag = false; |
|||
this.visibles[orderType].marketTotal = true; |
|||
this.msgList[orderType][3] = this.$t('nav.a4'); |
|||
} else if (this.total < this.minTotal) { |
|||
flag = false; |
|||
this.visibles.marketTotal = true; |
|||
this.msgList[3] = this.$t('nav.a5')+`${this.minTotal}`; |
|||
} |
|||
} |
|||
|
|||
if (order.direction == "sell") { // 卖 |
|||
if (this.empty(order.amount)) { |
|||
flag = false; |
|||
this.visibles[orderType].marketAmount = true; |
|||
this.msgList[orderType][4] = this.$t('nav.a6'); |
|||
} else if (order.amount < this.minQty) { |
|||
flag = false; |
|||
this.visibles[orderType].marketAmount = true; |
|||
this.msgList[orderType][4] = this.$t('nav.a7')+` ${this.minQty}`; |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case 3: // stop-limit |
|||
|
|||
if (this.empty(order.trigger_price)) { |
|||
flag = false; |
|||
this.visibles[orderType].trigger = true; |
|||
this.msgList[orderType][0] = this.$t('nav.a8') |
|||
} else if (this.empty(order.entrust_price)) { |
|||
flag = false; |
|||
this.visibles[orderType].limitPrice = true; |
|||
this.msgList[orderType][1] = this.$t('nav.a9') |
|||
} else if (this.empty(order.amount)) { |
|||
flag = false; |
|||
this.visibles[orderType].limitAmount = true; |
|||
this.msgList[orderType][2] = this.$t('nav.b1') |
|||
} |
|||
break; |
|||
|
|||
case 4: // stop-market |
|||
if (this.empty(order.trigger_price)) { |
|||
flag = false; |
|||
this.visibles[orderType].trigger = true; |
|||
this.msgList[orderType][0] = this.$t('nav.b2') |
|||
} else { |
|||
|
|||
if (order.direction == "buy") { |
|||
if (this.empty(total)) { |
|||
flag = false; |
|||
this.visibles[orderType].marketTotal = true; |
|||
this.msgList[orderType][3] = this.$t('nav.b3') |
|||
} else if (total < this.minTotal) { |
|||
flag = false; |
|||
this.visibles[orderType].marketTotal = true; |
|||
this.msgList[orderType][3] = this.$t('nav.b5')+` ${this.minTotal}` |
|||
} |
|||
} |
|||
|
|||
if (order.direction == "sell") { |
|||
if (this.empty(order.amount)) { |
|||
flag = false; |
|||
this.visibles[orderType].marketAmount = true; |
|||
this.msgList[orderType][4] = this.$t('nav.b6') |
|||
} else if (order.amount < this.minQty) { |
|||
flag = false; |
|||
this.visibles[orderType].marketAmount = true; |
|||
this.msgList[orderType][4] = this.$t('nav.b7')+` ${this.minQty}` |
|||
} |
|||
} |
|||
|
|||
} |
|||
break; |
|||
} |
|||
|
|||
// 清除所有样式 |
|||
this.clearAll(); |
|||
|
|||
// 返回验证结果 |
|||
return flag; |
|||
}, |
|||
|
|||
|
|||
}, |
|||
|
|||
created() { |
|||
|
|||
}, |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.buyPercentActive { |
|||
a:link { |
|||
font-weight: bold; |
|||
background: #26a69a; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.sellPercentActive { |
|||
a:link { |
|||
font-weight: bold; |
|||
background: #ef5350; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.input-group-text { |
|||
width: 68px; |
|||
@include flexible(row, center, center); |
|||
} |
|||
</style> |
|||
@ -0,0 +1,706 @@ |
|||
<template> |
|||
<!-- <div> --> |
|||
<!-- <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="container-fluid mtb15 no-fluid"> |
|||
<div class="row sm-gutters"> --> |
|||
|
|||
<!-- symbols --> |
|||
<!-- <symbols :marketList="marketList" :marketId="marketId" :isLogin="isLogin" :symbol.sync="symbol"> |
|||
</symbols> --> |
|||
|
|||
<!-- kline --> |
|||
<!-- <stock-kline :symbol="symbol" :socket="ws.socket" :priceDecimals="priceDecimals"></stock-kline> --> |
|||
|
|||
<!-- order book --> |
|||
<!-- <book-trades :priceCny="price_cny"></book-trades> --> |
|||
|
|||
<!-- 交易布局 --> |
|||
<!-- <make-deal :isLogin="isLogin" :pair="pair" :socket="ws.socket" :symbol="symbol" :buyorder="buyorder" |
|||
:sellorder="sellorder" :fromBalance="fromBalance" :toBalance="toBalance" :newTrade="newTrade" |
|||
:minQty="minQty" :minTotal="minTotal" :priceDecimals="priceDecimals" :qtyDecimals="qtyDecimals" |
|||
@update="update"></make-deal> --> |
|||
|
|||
<!-- market news --> |
|||
<!-- <market-news></market-news> --> |
|||
|
|||
<!-- order list --> |
|||
<!-- <order-list ref="Order" :ordersOpen="ordersOpen" :conditionOrders="conditionOrders" |
|||
:ordersHistory="ordersHistory" :holdPositionList="holdPositionList" :priceDecimals="priceDecimals" :qtyDecimals="qtyDecimals" |
|||
:isLogin="isLogin" :pair="pair" @change="currentTab = $event" @update="update" |
|||
@Pagination="page = $event" @Pagination1="page1 = $event"></order-list> --> |
|||
|
|||
<!-- </div> |
|||
</div> |
|||
</div> --> |
|||
<div class="contract-page" style="background-color: #000;"> |
|||
<div class="coin-change d-flex align-items-center py-2 pl-4 heading justify-content-between" style="background-color: #121212;"> |
|||
<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 marketList"> |
|||
<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="symbol = item.symbol" --> |
|||
<tr |
|||
v-for="item in parent.marketInfoList" |
|||
:key="item.symbol" |
|||
:class="{ active: item.symbol == symbol }" |
|||
@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.price}} |
|||
<!-- item.symbol == symbol ? price1 : --> |
|||
</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> --> |
|||
|
|||
<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> |
|||
<div class="page-top d-flex pt-2"> |
|||
<div class="kline-box flex-fill mr-2"> |
|||
<stock-kline :symbol="symbol" :socket="ws.socket" :priceDecimals="priceDecimals"></stock-kline> |
|||
</div> |
|||
<!-- 盘口 --> |
|||
<book-trades :priceCny="price_cny"></book-trades> |
|||
<!-- 交易 --> |
|||
<make-deal :isLogin="isLogin" :pair="pair" :socket="ws.socket" :symbol="symbol" :buyorder="buyorder" |
|||
:sellorder="sellorder" :fromBalance="fromBalance" :toBalance="toBalance" :newTrade="newTrade" |
|||
:minQty="minQty" :minTotal="minTotal" :priceDecimals="priceDecimals" :qtyDecimals="qtyDecimals" |
|||
@update="update"></make-deal> |
|||
</div> |
|||
<order-list ref="Order" :ordersOpen="ordersOpen" :conditionOrders="conditionOrders" |
|||
:ordersHistory="ordersHistory" :holdPositionList="holdPositionList" :priceDecimals="priceDecimals" :qtyDecimals="qtyDecimals" |
|||
:isLogin="isLogin" :pair="pair" @change="currentTab = $event" @update="update" |
|||
@Pagination="page = $event" @Pagination1="page1 = $event"></order-list> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Socket from '@/api/server/Socket.js'; |
|||
import Market from '@/api/market.js'; |
|||
import Exchange from "@/api/exchange"; |
|||
import Order from "@/api/order" |
|||
import Home from "@/api/home"; |
|||
import bus from "@/components/bus.js"; |
|||
export default { |
|||
|
|||
components: { |
|||
"Symbols": () => import( /* webpackChunkName:"symbols" */ "./symbols"), |
|||
"MakeDeal": () => import( /* webpackChunkName:"chart-deal" */ "./make-deal"), |
|||
"BookTrades": () => import( /* webpackChunkName:"order-book" */ "./book-trades"), |
|||
"MarketNews": () => import( /* webpackChunkName:"market-news" */ "./market-news"), |
|||
"OrderList": () => import( /* webpackChunkName:"order-list" */ "./order-list") |
|||
}, |
|||
|
|||
beforeCreate() { |
|||
|
|||
// this.ws = new Socket(`${this.Globals.Server.Path.WS}`); |
|||
|
|||
}, |
|||
|
|||
data() { |
|||
|
|||
return { |
|||
tabcolor:1, |
|||
price1:0, |
|||
isLianjie: false, |
|||
// `btcusdt` 不含`/`的小写 |
|||
symbol: this.$route.params.symbol || null, |
|||
marketId: null, |
|||
marketInfo: null, // 交易对基本信息 |
|||
pair: { |
|||
from: '-', |
|||
to: '-', |
|||
}, |
|||
marketList: [], // 在symbol组件中 需要遍历 因此默认数组避免出错 |
|||
trade: [], |
|||
pcBannerList: [], |
|||
newTrade: null, |
|||
|
|||
// 所有的数据 都统一按照价格从小到大排序 |
|||
buyList: [], |
|||
sellList: [], |
|||
|
|||
// 初始化精度值和交易 |
|||
priceDecimals: 0, |
|||
qtyDecimals: 0, |
|||
minQty: 0, |
|||
minTotal: 0, |
|||
|
|||
now: null, |
|||
|
|||
// 下单情况 可由orderbook跨页面定制 |
|||
buyorder: { |
|||
trigger_price: '', // 触发价 |
|||
entrust_price: '', // 委托价 |
|||
amount: 0, // 数量 |
|||
direction: "buy", // 方向 |
|||
}, |
|||
|
|||
sellorder: { |
|||
entrust_price: '', |
|||
trigger_price: '', |
|||
amount: 0, |
|||
direction: "sell", |
|||
}, |
|||
|
|||
// 从order传递price |
|||
passOrderPrice: true, |
|||
|
|||
// 各种委托单 |
|||
ordersOpen: { |
|||
total: 0, |
|||
}, |
|||
|
|||
ordersHistory: { |
|||
total: 0, |
|||
}, |
|||
|
|||
holdPositionList: { |
|||
total: 0, |
|||
}, |
|||
|
|||
conditionOrders: { |
|||
total: 0, |
|||
}, |
|||
|
|||
// 用户钱包余额 |
|||
fromBalance: 0, |
|||
toBalance: 0, |
|||
|
|||
// 当前页面socket |
|||
ws: null, |
|||
|
|||
// 是否开启交易密码 |
|||
transPwdEnabled: false, |
|||
|
|||
currentTab: "stockholdPosition", // orders显示的tab |
|||
price_cny: 0, |
|||
page: 1, |
|||
page1: 1, |
|||
timer: null |
|||
} |
|||
|
|||
}, |
|||
|
|||
computed: { |
|||
|
|||
// 当前语言 |
|||
lang() { |
|||
let browser_Lang = navigator.language.includes('zh') ? 'cn' : 'en', |
|||
lang = localStorage.lang || browser_Lang; |
|||
return lang; |
|||
}, |
|||
|
|||
isLogin() { |
|||
return Boolean(this.userAuth); |
|||
}, |
|||
|
|||
userAuth() { |
|||
const auth = localStorage.getItem("auth"); |
|||
let ret = ""; |
|||
if (auth) { |
|||
let { |
|||
memberId, |
|||
accessToken |
|||
} = JSON.parse(auth); |
|||
ret = `?${accessToken}&${memberId}`; |
|||
} |
|||
return ret; |
|||
}, |
|||
|
|||
userInfo() { |
|||
if (this.isLogin) { |
|||
return JSON.parse(localStorage.getItem("auth")); |
|||
} else { |
|||
return { |
|||
user_id: 0, |
|||
}; |
|||
} |
|||
}, |
|||
activeContract(val) { |
|||
let marketList=this.marketList |
|||
.map(item => item.marketInfoList) |
|||
.flat() |
|||
.find(item => item.symbol == this.symbol) || {} |
|||
if(val.price){ |
|||
delete marketList.price |
|||
} |
|||
return marketList; |
|||
}, |
|||
}, |
|||
|
|||
watch: { |
|||
// 切换symbol时更新路由 |
|||
symbol(newVal, oldVal) { |
|||
|
|||
// 取消订阅 或者关闭连接 防止干扰下次值 |
|||
if (oldVal) this.unsub(oldVal); |
|||
|
|||
// 还原页面状态 避免造成数据值紊乱 |
|||
this.trade = []; |
|||
this.sellList = []; |
|||
this.buyList = []; |
|||
this.newTrade = null; |
|||
|
|||
// 买卖单数据结构 |
|||
this.buyorder = { |
|||
trigger_price: '', |
|||
entrust_price: '', |
|||
amount: 0, |
|||
direction: "buy", |
|||
}; |
|||
this.sellorder = { |
|||
trigger_price: '', |
|||
entrust_price: '', |
|||
amount: 0, |
|||
direction: "sell", |
|||
}; |
|||
|
|||
// 请求新的页面数据 |
|||
if (newVal) this.$router.push(`/exchangestock/${newVal}`); |
|||
}, |
|||
|
|||
marketInfo(newVal, oldVal) { |
|||
if (newVal && !oldVal) { // 第一次取得marketInfo时 更新接口 主要针对刷新页面 |
|||
this.update(); |
|||
} |
|||
}, |
|||
|
|||
// 加载新页面 重新订阅所有数据 |
|||
$route( /*newRouter, oldRouter*/ ) { |
|||
|
|||
this.addSub(); |
|||
|
|||
// 初始化market信息 |
|||
this.findMarketBySymbol(); |
|||
|
|||
// 更新所有接口数据 |
|||
this.update(); |
|||
|
|||
}, |
|||
|
|||
currentTab() { |
|||
// tab切换时自动更新 |
|||
this.getOrders(); |
|||
}, |
|||
page() { |
|||
if (this.currentTab == "histories") { // 历史委托 |
|||
this.getHistories(); |
|||
} |
|||
}, |
|||
// page1() { |
|||
// if (this.currentTab == "stockholdPosition") { // 目前持仓 |
|||
// this.stockholdPosition() |
|||
// } |
|||
// } |
|||
}, |
|||
|
|||
methods: { |
|||
ispopover1(item){ |
|||
this.symbol=item; |
|||
}, |
|||
initMarket() { |
|||
// 初始化订阅marketList数据 |
|||
this.ws.send({ |
|||
"cmd": "sub", |
|||
"msg": 'exchangeMarketList', |
|||
}) |
|||
}, |
|||
|
|||
// 订阅当前的symbol |
|||
addSub() { |
|||
|
|||
this.ws.send([{ |
|||
cmd: 'sub', |
|||
msg: `sellList_${this.symbol}` |
|||
}, { |
|||
cmd: 'sub', |
|||
msg: `buyList_${this.symbol}` |
|||
}, { |
|||
cmd: 'req', |
|||
msg: `tradeList_${this.symbol}` |
|||
}, { |
|||
cmd: 'sub', |
|||
msg: `tradeList_${this.symbol}` |
|||
}]); |
|||
|
|||
}, |
|||
|
|||
// 取消指定的symbol |
|||
unsub(symbol) { |
|||
|
|||
this.ws.send([{ |
|||
cmd: 'unsub', |
|||
msg: `sellList_${symbol}` |
|||
}, { |
|||
cmd: 'unsub', |
|||
msg: `buyList_${symbol}` |
|||
}, { |
|||
cmd: 'unsub', |
|||
msg: `tradeList_${symbol}` |
|||
}]); |
|||
|
|||
}, |
|||
|
|||
// 在交易列表中 查找当前id记录 并将行情信息写入全局 |
|||
// 以便在其他组件中 直接使用 |
|||
findMarketBySymbol() { |
|||
|
|||
let isKeep = true; // 退出外层循环的标识符 |
|||
|
|||
for (let coin of this.marketList) { |
|||
|
|||
for (let market of coin.marketInfoList) { |
|||
|
|||
// 查询交易对名称 |
|||
if (market.symbol === this.symbol) { |
|||
|
|||
// 转化pair为集合 便于提取 |
|||
this.pair = { |
|||
to: market.coin_name, // 右币 |
|||
from: coin.coin_name // 左币 |
|||
}; |
|||
|
|||
// 创建marketId |
|||
this.marketId = market.pair_id; |
|||
|
|||
// 写入market信息 |
|||
this.marketInfo = market; |
|||
|
|||
// 未有选定Tab时 默认一个now 后期从子组件中取 |
|||
if (!this.now) this.now = coin.coin_name; |
|||
|
|||
// 写入数据值精度 用来显示截取长度 |
|||
this.priceDecimals = market.price_decimals; |
|||
this.qtyDecimals = market.qty_decimals; |
|||
|
|||
// 下单时 需要验证最小数量和最小总值 |
|||
this.minTotal = market.min_total; |
|||
this.minQty = market.min_qty; |
|||
|
|||
isKeep = false; |
|||
|
|||
// 终止内部循环 |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// 终止外部循环 |
|||
if (!isKeep) break; |
|||
} |
|||
}, |
|||
|
|||
getBalance() { |
|||
|
|||
if (this.isLogin) { |
|||
|
|||
Exchange.getstockUserBalance(this.marketInfo.pair_name).then(data => { |
|||
|
|||
this.fromBalance = data[this.pair.from.toUpperCase()].usable_balance; |
|||
this.toBalance = data[this.pair.to.toUpperCase()].usable_balance; |
|||
|
|||
}).catch(err => { |
|||
|
|||
}); |
|||
} |
|||
|
|||
}, |
|||
|
|||
// 更新接口数据 |
|||
update() { |
|||
this.getBalance(); |
|||
this.getOrders(); |
|||
this.getCurrencyExCny(); |
|||
}, |
|||
// 获取汇率 |
|||
getCurrencyExCny() { |
|||
if (!this.pair.from || this.pair.from == '-') return; |
|||
Exchange.getCurrencyExCny({ |
|||
coin_name: this.pair.from |
|||
}).then(res => { |
|||
this.price_cny = res.price_cny |
|||
}) |
|||
}, |
|||
getOrders() { |
|||
|
|||
if (this.currentTab == "opens") { // 当前委托 |
|||
|
|||
this.getOpens(); |
|||
|
|||
} else if (this.currentTab == "conditions") { // 条件委托 |
|||
|
|||
this.getConditions(); |
|||
|
|||
} else if (this.currentTab == "histories") { // 历史委托 |
|||
|
|||
this.getHistories(); |
|||
|
|||
} else if (this.currentTab == "stockholdPosition") { |
|||
this.stockholdPosition() |
|||
this.timer = setInterval(()=>{ |
|||
this.stockholdPosition() |
|||
}, 3000) |
|||
} |
|||
|
|||
// console.log("---订单更新成功---"); |
|||
|
|||
}, |
|||
|
|||
getOpens() { |
|||
if (this.isLogin) { |
|||
Order.getstockCurrentEntrust({ |
|||
symbol: this.marketInfo.pair_name, |
|||
}).then(data => { |
|||
this.ordersOpen = data |
|||
}).catch(err => { |
|||
// console.log(err) |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
getConditions() { |
|||
if (this.isLogin) { |
|||
Order.getConditionEntrust({ |
|||
symbol: this.marketInfo.pair_name, |
|||
}).then(data => { |
|||
this.conditionOrders = data; |
|||
}).catch(err => { |
|||
// console.log(err) |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
getHistories() { |
|||
if (this.isLogin) { |
|||
Order.getstockHistoryEntrust({ |
|||
symbol: this.marketInfo.pair_name, |
|||
page:this.page |
|||
}) |
|||
.then(data => { |
|||
this.ordersHistory = data; |
|||
}) |
|||
.catch(err => {}) |
|||
} |
|||
}, |
|||
|
|||
stockholdPosition(){ |
|||
if (this.isLogin) { |
|||
let data = { |
|||
symbol: this.marketInfo.pair_name, |
|||
page:this.page1 |
|||
}; |
|||
Exchange.stockholdPosition(data ,{ loading: false }).then(data => { |
|||
this.holdPositionList = data; |
|||
}).catch(err => {}) |
|||
} |
|||
}, |
|||
|
|||
indexList() { |
|||
Home.indexList().then((res) => { |
|||
this.pcBannerList = res.pcBannerList |
|||
setTimeout(() => { |
|||
this.skroll(); |
|||
}, 100); |
|||
}).catch((res) => {}); |
|||
}, |
|||
initWs() { |
|||
// this.ws = new Socket(`${this.Globals.Server.Path.WS}`); |
|||
this.ws = new Socket(`${this.Globals.Server.Path.WS2}`); |
|||
this.ws.on("open", () => { |
|||
if (this.isLianjie) { |
|||
bus.$emit('sendMsg', this.ws.socket); |
|||
this.isLianjie = false |
|||
} |
|||
// 连接成功后 初始化订阅市场信息 |
|||
this.initMarket(); |
|||
// 如果指定了id 订阅该行情的所有其他信息 |
|||
if (this.symbol) this.addSub(); |
|||
}) |
|||
|
|||
this.ws.on("message", (response) => { |
|||
|
|||
let { |
|||
data, // 返回数据 |
|||
sub, // 订阅标签 |
|||
type, // 返回类型 |
|||
msg, // 提示信息 |
|||
code, // 错误代号 |
|||
} = response; |
|||
|
|||
// 答复心跳 保持连接 |
|||
if (type == "ping") this.ws.send({ |
|||
type: "pong" |
|||
}); |
|||
|
|||
switch (sub) { |
|||
|
|||
case "exchangeMarketList": |
|||
|
|||
this.marketList = data; |
|||
|
|||
if (!this.symbol) { // 默认symbol 重新请求数据 |
|||
this.symbol = _.nth(data).marketInfoList[0].symbol; |
|||
} else { // 找出该symbol对应的记录并更新页面 |
|||
this.findMarketBySymbol(); |
|||
} |
|||
|
|||
break; |
|||
|
|||
case `buyList_${this.symbol}`: |
|||
|
|||
this.buyList = data; |
|||
break; |
|||
|
|||
case `sellList_${this.symbol}`: |
|||
|
|||
this.sellList = data; |
|||
break; |
|||
|
|||
case `tradeList_${this.symbol}`: |
|||
if (type == "history") { // 历史记录 |
|||
this.trade = this.trade.concat(data); |
|||
} else if (type == "dynamic") { // 有更新就推送 |
|||
this.trade.unshift(data); |
|||
} |
|||
// 最新交易 更新用于余额和订单记录 |
|||
this.newTrade = _.nth(this.trade); |
|||
if (this.newTrade && this.newTrade.buy_user_id === this.userInfo.user_id || this |
|||
.newTrade.sell_user_id == this.userInfo.user_id) this.update(); |
|||
break; |
|||
} |
|||
}); |
|||
|
|||
this.ws.on("close", () => { |
|||
// this.$message({ |
|||
// type: "error", |
|||
// message: this.$t("nav.b8"), |
|||
// duration: 2000, |
|||
// }); |
|||
this.isLianjie = true |
|||
this.initWs() |
|||
// if (this.symbol) this.getCurrencyExCny(); |
|||
// this.indexList() |
|||
|
|||
}); |
|||
} |
|||
}, |
|||
|
|||
created() { |
|||
|
|||
this.initWs() |
|||
if (this.symbol) this.getCurrencyExCny(); |
|||
this.indexList() |
|||
|
|||
Market.getstockMarketList().then(data => { |
|||
this.marketList = data; |
|||
}).catch(err => {}); |
|||
// this.update(); |
|||
}, |
|||
beforeDestroy() { |
|||
clearInterval(this.timer); |
|||
}, |
|||
|
|||
mounted() { |
|||
// if (this.symbol) this.update(); |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.el-popover{ |
|||
background-color: #121212; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,824 @@ |
|||
<template> |
|||
<div class="col-md-3" style="background-color: #121212;"> |
|||
<div class="px-4 py-2 heading"> |
|||
<!-- <theme-change/> --> |
|||
<el-tooltip placement="bottom" effect="light"> |
|||
<div slot="content" class="market"> |
|||
<div class="coin p-md color-light fn-20"> |
|||
<img :src="detail.coin_icon" width="20" height="20" alt=""> |
|||
{{detail.full_name}} |
|||
</div> |
|||
<div class="list"> |
|||
<div class="d-flex justify-between p-x-md p-y-xs"> |
|||
<div>{{$t('nav.c3')}}:</div> |
|||
<div class="color-light"> |
|||
{{detail.total_issuance}} |
|||
</div> |
|||
</div> |
|||
<div class="d-flex justify-between p-x-md p-y-xs"> |
|||
<div>{{$t('nav.c4')}}:</div> |
|||
<div class="color-light"> |
|||
{{detail.total_circulation}} |
|||
</div> |
|||
</div> |
|||
<div class="d-flex justify-between p-x-md p-y-xs"> |
|||
<div>{{$t('nav.c5')}}:</div> |
|||
<div class="color-light"> |
|||
{{detail.crowdfunding_price}} |
|||
</div> |
|||
</div> |
|||
<div class="d-flex justify-between p-x-md p-y-xs"> |
|||
<div>{{$t('nav.c6')}}:</div> |
|||
<div class="color-light"> |
|||
{{detail.publish_time}} |
|||
</div> |
|||
</div> |
|||
<div class=" p-x-md p-y-xs"> |
|||
<div>{{$t('nav.c7')}}:</div> |
|||
<div class="color-light ov"> |
|||
{{detail.white_paper_link}} |
|||
</div> |
|||
</div> |
|||
<div class=" p-x-md p-y-xs"> |
|||
<div>{{$t('nav.c8')}}:</div> |
|||
<div class="color-light ov"> |
|||
{{detail.official_website_link}} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="p-md"> |
|||
<div class="fn-20 color-light"> |
|||
{{$t('nav.c9')}} |
|||
</div> |
|||
<div class="p-y-md edit-content" v-html="detail.coin_content"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- <el-button><img src="../../assets/img/shuoming.png" width="20" height="20" alt=""></el-button> --> |
|||
</el-tooltip> |
|||
|
|||
</div> |
|||
<!-- 生成K线图 --> |
|||
<!-- <stock-kline :symbol="symbol" :socket="socket" :priceDecimals="priceDecimals"></stock-kline> --> |
|||
|
|||
<!-- 交易处理 --> |
|||
<div class="market-trade "> |
|||
<h4 style="text-align: center;">{{$t('nav.c12')}}</h4> |
|||
<div class="d-flex" style="margin: 14px;justify-content: space-around;border: 1px solid #b9b9b9;border-radius: 40px;"> |
|||
<div :class="Tab_buysell == 1?'Tab_buy':'Tab_buy1'" @click="Tab_buysell=1">{{$t('order.buy')}}</div> |
|||
<div :class="Tab_buysell == 2?'Tab_sell':'Tab_sell1'" @click="Tab_buysell=2">{{$t('order.sell')}}</div> |
|||
</div> |
|||
<ul class="nav nav-pills"> |
|||
<li class="nav-item"> |
|||
<a href :class="[`nav-link`, {active:!isCondition&&isMarket}]" @click.prevent="isCondition=false;isMarket=true;" style="background-color: transparent;"> |
|||
{{ $t("common.market") }} |
|||
</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a href :class="[`nav-link`, {active:!isCondition&&!isMarket}]" @click.prevent="isCondition=false;isMarket=false;" style="background-color: transparent;"> |
|||
{{ $t("common.limit") }}</a> |
|||
</li> |
|||
|
|||
<!-- <li class="nav-item"> |
|||
<a href :class="[`nav-link`, {active:isCondition&&!isMarket}]" @click.prevent="isCondition=true;isMarket=false;"> |
|||
{{ $t("common.stop-limit") }}</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a href :class="[`nav-link`, {active:isCondition&&isMarket}]" @click.prevent="isCondition=true;isMarket=true;"> |
|||
{{ $t("common.stop-market") }}</a> |
|||
</li> --> |
|||
</ul> |
|||
<div class="tab-content"> |
|||
<div class="tab-pane fade show active"> |
|||
<div class="d-flex justify-content-between"> |
|||
<!------------- Buy Order --------------> |
|||
<div class="market-trade-buy" v-if="Tab_buysell == 1"> |
|||
|
|||
<!-- Trigger Price --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.trigger"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.buy[0] }} |
|||
</span> |
|||
<div class="input-group" v-if="isCondition" slot="reference"> |
|||
<input type="number" style="background-color: #121212;" v-model="buyorder.trigger_price" class="form-control" :placeholder="$t('exchange.trigger-price')"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.from}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
|
|||
<template v-if="isMarket"> |
|||
<div class="input-group"> |
|||
<!-- Market Price --> |
|||
<input type="text" class="form-control" disabled :placeholder="$t('exchange.at-best-price')"> |
|||
</div> |
|||
</template> |
|||
<template v-else> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.limitPrice"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.buy[1] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<!-- Entrust Price --> |
|||
<input type="number" v-model="buyorder.entrust_price" class="form-control" :placeholder="$t('exchange.enter-price')"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.from}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
|
|||
<!-- Amount/Total --> |
|||
<template v-if="isMarket"> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.marketTotal"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.buy[3] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<!-- <input type="number" v-model="buyTotal" class="form-control" min=0 :placeholder="$t('exchange.enter-total')"> --> |
|||
<input type="number" v-model="buyorder.amount" class="form-control" min=0 :placeholder="$t('exchange.enter-total')"> |
|||
<div class="input-group-append"> |
|||
<!-- <span class="input-group-text">{{pair.from}}</span> --> |
|||
<span class="input-group-text">{{pair.to}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
<template v-else> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.limitAmount"> |
|||
<!-- 提示框的内容 --> |
|||
<span class="content"> |
|||
<!-- 图标 --> |
|||
<i class="el-icon-warning-outline"></i> |
|||
<!-- 提示 --> |
|||
{{ msgList.buy[2] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<input type="number" v-model="buyorder.amount" class="form-control" min=0 :placeholder="$t('exchange.enter-amount')"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.to}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
|
|||
<!-- Percent --> |
|||
<ul class="market-trade-list"> |
|||
<li v-for="(item,index) in percentage" :key="index" :class="{buyPercentActive:index == buyPercentIndex}" @click="renderBuyAmount(item.value, index)"> |
|||
<a href="javascript:void 0">{{ item.label }}</a> |
|||
</li> |
|||
</ul> |
|||
|
|||
<!-- Total --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.buy.orderTotal"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.buy[5] }} |
|||
</span> |
|||
<p slot="reference"> |
|||
{{$t('exchange.order-total')}} |
|||
<span> <b>{{buyTotal}}</b> {{pair.from}} </span> |
|||
</p> |
|||
</el-popover> |
|||
|
|||
<!-- Available --> |
|||
<p> |
|||
{{ $t('exchange.amount') }} |
|||
<span> {{toBalance}} {{pair.to}}</span><br /> |
|||
{{ $t('exchange.balance') }} |
|||
<span> {{fromBalance}} {{pair.from}}</span> |
|||
</p> |
|||
<button class="btn buy" @click="handleBuyOrder">{{ $t("common.buy") }} {{pair.to}}</button> |
|||
</div> |
|||
|
|||
<!-- Sell Order --> |
|||
<div class="market-trade-buy" v-if="Tab_buysell == 2"> |
|||
<!-- class="market-trade-sell" --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.sell.trigger"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.sell[0] }} |
|||
</span> |
|||
<!-- Trigger Price --> |
|||
<div class="input-group" v-if="isCondition" slot="reference"> |
|||
<input type="number" v-model="sellorder.trigger_price" class="form-control" min=0 :placeholder="$t('exchange.trigger-price')"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.from}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
|
|||
<template v-if="isMarket"> |
|||
<div class="input-group"> |
|||
<!-- Market Price --> |
|||
<input type="text" class="form-control" disabled :placeholder="$t('exchange.at-best-price')"> |
|||
</div> |
|||
</template> |
|||
<template v-else> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.sell.limitPrice"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.sell[1] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<!-- Entrust Price --> |
|||
<input type="number" v-model="sellorder.entrust_price" class="form-control" min=0 :placeholder="$t('exchange.enter-price')"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.from}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
</template> |
|||
|
|||
<!-- Amount --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.sell.limitAmount"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.sell[2] }} |
|||
</span> |
|||
<div class="input-group" slot="reference"> |
|||
<input type="number" v-model="sellorder.amount" class="form-control" min=0 :placeholder="$t('exchange.enter-amount')"> |
|||
<div class="input-group-append"> |
|||
<span class="input-group-text">{{pair.to}}</span> |
|||
</div> |
|||
</div> |
|||
</el-popover> |
|||
|
|||
<ul class="market-trade-list"> |
|||
<li v-for="(item,index) in percentage" :key="index" :class="{sellPercentActive:index == sellPercentIndex}" @click="renderSellAmount(item.value, index)"> |
|||
<a href="javascript:void 0">{{ item.label }}</a> |
|||
</li> |
|||
</ul> |
|||
|
|||
<!-- Total --> |
|||
<el-popover popper-class='popover-tips' placement="top-start" trigger="manual" v-model="visibles.sell.orderTotal"> |
|||
<span class="content"> |
|||
<i class="el-icon-warning-outline"></i> |
|||
{{ msgList.sell[5] }} |
|||
</span> |
|||
<p slot="reference"> |
|||
{{ $t('exchange.order-total') }} |
|||
<span> |
|||
<b>{{sellTotal}}</b> {{pair.from}} </span> |
|||
</p> |
|||
</el-popover> |
|||
|
|||
<p> |
|||
{{$t('exchange.amount')}} |
|||
<span> {{toBalance}} {{pair.to}} </span><br /> |
|||
{{ $t('exchange.balance') }} |
|||
<span> {{fromBalance}} {{pair.from}}</span> |
|||
</p> |
|||
<button class="btn sell" @click="handleSellOrder">{{ $t("common.sell") }} {{pair.to}}</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Exchange from "@/api/exchange"; |
|||
import Market from "@/api/market"; |
|||
export default { |
|||
|
|||
props: ["isLogin", "socket", "symbol", "pair", "buyorder", "sellorder", "fromBalance", "toBalance", "newTrade", "minQty", "minTotal", "priceDecimals", "qtyDecimals"], |
|||
|
|||
data() { |
|||
return { |
|||
Tab_buysell:1, |
|||
detail:[], |
|||
isMarket: true, |
|||
isCondition: false, |
|||
userBanlance: null, |
|||
|
|||
cacheTotal: 0, // 存放市价单输入的总值 |
|||
cachePrice: null, // 存放最新价格 |
|||
|
|||
// 气泡弹框内容和触发 |
|||
visibles: { |
|||
"buy": { |
|||
limitPrice: false, // 限制价格 |
|||
limitAmount: false, // 限制数量 |
|||
marketTotal: false, // 限制总额 |
|||
marketAmount: false, // 市价数量 |
|||
trigger: false, // 触发价格 |
|||
orderTotal: false, // 订单总额 |
|||
}, |
|||
"sell": { |
|||
limitPrice: false, |
|||
limitAmount: false, |
|||
marketTotal: false, |
|||
marketAmount: false, |
|||
trigger: false, |
|||
orderTotal: false, |
|||
}, |
|||
}, |
|||
msgList: { |
|||
"buy": Array(6).fill(''), // 5个空字符串的数组 |
|||
"sell": Array(6).fill('') |
|||
}, |
|||
// 百分比集合 |
|||
percentage: [{ |
|||
label: "25%", |
|||
value: 0.25 |
|||
}, |
|||
{ |
|||
label: "50%", |
|||
value: 0.5 |
|||
}, |
|||
{ |
|||
label: "75%", |
|||
value: 0.75 |
|||
}, |
|||
{ |
|||
label: "100%", |
|||
value: 1 |
|||
}, |
|||
], |
|||
// |
|||
buyPercentIndex: null, |
|||
sellPercentIndex: null, |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
|
|||
// 获取type类型值 |
|||
type() { |
|||
if (!this.isCondition) { |
|||
return this.isMarket ? 2 : 1; |
|||
} else { |
|||
return this.isMarket ? 4 : 3; |
|||
} |
|||
}, |
|||
|
|||
theme() { |
|||
return localStorage.theme ? localStorage.theme : "light"; |
|||
}, |
|||
|
|||
buyTotal: { |
|||
get() { |
|||
if (!this.isMarket) { |
|||
return Math.multiple(this.buyorder.entrust_price, this.buyorder.amount); |
|||
} else { |
|||
// return this.cacheTotal; |
|||
return Math.multiple(this.buyorder.entrust_price, this.buyorder.amount); |
|||
} |
|||
}, |
|||
set(val) { |
|||
// 根据总值 计算数量 |
|||
if (!this.isMarket) { |
|||
this.buyorder.amount = Math.division(val, this.buyorder.entrust_price); |
|||
} else { // 市价单 缓存总值 |
|||
this.cacheTotal = val; |
|||
this.buyorder.amount = this.cacheTotal |
|||
} |
|||
} |
|||
}, |
|||
|
|||
sellTotal: { |
|||
get() { |
|||
return Math.multiple(this.sellorder.entrust_price, this.sellorder.amount); |
|||
} |
|||
}, |
|||
|
|||
// 当前语言 |
|||
lang() { |
|||
let browser_Lang = navigator.language.includes('zh') ? 'zh' : 'en'; |
|||
return localStorage.lang || browser_Lang; |
|||
}, |
|||
|
|||
}, |
|||
|
|||
watch: { |
|||
custom(newVal) { |
|||
// console.log(newVal) |
|||
}, |
|||
|
|||
type() { |
|||
// 切换订单类型时 清空价格和数量 |
|||
// 数量改变时 触发percentage更新 |
|||
this.reset(); |
|||
}, |
|||
|
|||
// 有新交易时触发 给限价单设定初始价格 |
|||
newTrade(newVal, oldVal) { |
|||
// if (!this.isMarket ) { |
|||
|
|||
// 新交易对有trade数据 |
|||
if (!oldVal && newVal) { // 没值到初始化值 |
|||
// 修改order里面price的值 |
|||
this.cachePrice = newVal.price; |
|||
this.reset(); |
|||
} |
|||
|
|||
// 切换交易对时 清空了数据 |
|||
if (oldVal && !newVal) { // 有值到没值的过程 |
|||
this.cachePrice = null; |
|||
this.reset(); |
|||
} |
|||
// } |
|||
}, |
|||
symbol(){ |
|||
this.getCoinInfo() |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
getCoinInfo(){ |
|||
// console.log(this.symbol.indexOf('usdt')!=-1) |
|||
|
|||
// if(!this.symbol){ |
|||
// return; |
|||
// } |
|||
if(this.symbol.indexOf('usdt')!=-1){ |
|||
var market=this.symbol.substring(0,this.symbol.length-4); |
|||
}else{ |
|||
var market=this.symbol.substring(0,this.symbol.length-3); |
|||
} |
|||
// console.log(market) |
|||
let data = { |
|||
coin_name:market, |
|||
// coin_name:this.symbol, |
|||
lang:this.lang |
|||
} |
|||
Market.getstockCoinInfo(data).then(res => { |
|||
this.detail = res |
|||
}).catch(err => { |
|||
|
|||
}); |
|||
}, |
|||
renderBuyAmount(val, index) { |
|||
// console.Info(val) |
|||
// console.Info(this.buyorder) |
|||
if (!this.buyorder.entrust_price) { |
|||
this.visibles.buy.limitPrice = true; |
|||
this.msgList.buy[1] = this.$t('nav.set'); |
|||
this.clearAll(); |
|||
return; |
|||
} |
|||
this.buyPercentIndex = index; |
|||
this.buyTotal = Math.multiple(this.fromBalance, val); |
|||
}, |
|||
|
|||
renderSellAmount(val, index) { |
|||
if (!this.sellorder.entrust_price) { |
|||
this.visibles.sell.limitPrice = true; |
|||
this.msgList.sell[1] = this.$t('nav.set'); |
|||
this.clearAll(); |
|||
return; |
|||
} |
|||
this.sellPercentIndex = index; |
|||
this.sellorder.amount = Math.multiple(this.toBalance, val); |
|||
}, |
|||
|
|||
handleBuyOrder() { |
|||
|
|||
// 执行前端的有效性验证 |
|||
if (!this.chkValidate(this.buyorder, this.buyTotal, "buy")) return; |
|||
|
|||
const baseArgs = { |
|||
symbol: this.pair.to.concat('/', this.pair.from), |
|||
type: this.type, |
|||
}; |
|||
|
|||
// 区分限价、市价和条件委托单 |
|||
// 1、限价单买入时 需要的参数:价格、数量、 |
|||
// 2、市价单买入时 需要的参数:总值 |
|||
// 3、条件单买入时 需要的参数:触发价、价格数量或者总值 |
|||
// 4、卖出时 需要价格和数量 |
|||
Exchange.stockstoreEntrust(Object.assign(this.buyorder, { |
|||
total: this.buyTotal, |
|||
}, baseArgs)).then(data => { |
|||
this.$message({ |
|||
message:'success', |
|||
type: "success" |
|||
}); |
|||
// 触发父组件的方法 更新余额和订单 |
|||
this.$emit('update'); |
|||
// 清空表单 |
|||
this.reset(); |
|||
}).catch(err => { |
|||
|
|||
}); |
|||
}, |
|||
|
|||
handleSellOrder() { |
|||
// 执行前端的有效性验证 |
|||
if (!this.chkValidate(this.sellorder, this.sellTotal, "sell")) return; |
|||
// console.info(this.sellTotal) |
|||
const baseArgs = { |
|||
symbol: this.pair.to.concat('/', this.pair.from), |
|||
type: this.type, |
|||
}; |
|||
Exchange.stockstoreEntrust(Object.assign(this.sellorder, { |
|||
total: this.sellTotal |
|||
}, baseArgs)).then(data => { |
|||
this.$message({ |
|||
message:'success', |
|||
type: "success" |
|||
}); |
|||
// 触发父组件的方法 更新余额和订单 |
|||
this.$emit('update'); |
|||
// 清空表单 |
|||
this.reset(); |
|||
}).catch(err => { |
|||
|
|||
}); |
|||
}, |
|||
|
|||
reset() { |
|||
|
|||
// 如果没有newTrade时 则没有缓存价格 重置为最小值0 |
|||
// 由于输入框去除了精度空值 这里还需要手动设置精度值 |
|||
let price = this.cachePrice || 0; |
|||
// console.info(price) |
|||
// console.info(this.priceDecimals) |
|||
this.buyorder.entrust_price = Math.omitTo(price, this.priceDecimals); |
|||
this.buyorder.trigger_price = Math.omitTo(price, this.priceDecimals); |
|||
|
|||
this.sellorder.entrust_price = Math.omitTo(price, this.priceDecimals); |
|||
// console.info(this.sellorder.entrust_price) |
|||
this.sellorder.trigger_price = Math.omitTo(price, this.priceDecimals); |
|||
|
|||
// 清空数量 |
|||
this.buyorder.amount = 0; |
|||
this.sellorder.amount = 0; |
|||
|
|||
// total赋值会触发set方法 |
|||
// this.buyTotal = 0; |
|||
// this.cacheTotal = 0; |
|||
|
|||
// 去除百分比样式 |
|||
this.buyPercentIndex = -1; |
|||
this.sellPercentIndex = -1; |
|||
}, |
|||
|
|||
clearAll() { |
|||
// 5s后统一清除提示框 |
|||
setTimeout(function () { |
|||
Object.keys(this.visibles.buy).forEach(key => this.visibles.buy[key] = false); |
|||
Object.keys(this.visibles.sell).forEach(key => this.visibles.sell[key] = false); |
|||
}.bind(this), 5000); |
|||
}, |
|||
|
|||
empty(val) { |
|||
let ret; |
|||
switch (typeof val) { |
|||
case "number": |
|||
ret = val == 0; |
|||
break; |
|||
case "string": |
|||
ret = val == "0" || /^\s?$/.test(val); |
|||
break; |
|||
case "boolean": |
|||
ret = val; |
|||
break; |
|||
default: |
|||
ret = Boolean(val); |
|||
break; |
|||
} |
|||
return ret; |
|||
}, |
|||
|
|||
chkValidate(order, total, orderType) { |
|||
// |
|||
// 验证登录 |
|||
if (!this.isLogin) { |
|||
this.$confirm(this.$t('nav.login'), { |
|||
confirmButtonText:this.$t('common.confirmBtn'), |
|||
cancelButtonText: this.$t('common.cancelBtn'), |
|||
type: 'warning' |
|||
}).then(() => { |
|||
this.$router.push(`/sign-in`); |
|||
}).catch(); |
|||
return; |
|||
} |
|||
|
|||
let flag = true; |
|||
|
|||
switch (this.type) { |
|||
|
|||
case 1: // limit |
|||
if (this.empty(order.entrust_price)) { |
|||
flag = false; |
|||
this.visibles[orderType].limitPrice = true; |
|||
this.msgList[orderType][1] = this.$t('nav.a1'); |
|||
} else if (this.empty(order.amount)) { |
|||
flag = false; |
|||
this.visibles[orderType].limitAmount = true; |
|||
this.msgList[orderType][2] = this.$t('nav.a2'); |
|||
} else { |
|||
if (order.amount < this.minQty) { |
|||
flag = false; |
|||
this.visibles[orderType].limitAmount = true; |
|||
this.msgList[orderType][2] = this.$t('nav.a3')+`${this.minQty}`; |
|||
} |
|||
|
|||
if (total < this.minTotal) { |
|||
flag = false; |
|||
this.visibles[orderType].orderTotal = true; |
|||
this.msgList[orderType][5] = this.$t('exchange.total')+`${this.minTotal}`; |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case 2: // market |
|||
if (order.direction == "buy") { // 买 |
|||
if (this.empty(total)) { |
|||
flag = false; |
|||
this.visibles[orderType].marketTotal = true; |
|||
this.msgList[orderType][3] = this.$t('nav.a4'); |
|||
} else if (this.total < this.minTotal) { |
|||
flag = false; |
|||
this.visibles.marketTotal = true; |
|||
this.msgList[3] = this.$t('nav.a5')+` ${this.minTotal}`; |
|||
} |
|||
} |
|||
|
|||
if (order.direction == "sell") { // 卖 |
|||
if (this.empty(order.amount)) { |
|||
flag = false; |
|||
this.visibles[orderType].marketAmount = true; |
|||
this.msgList[orderType][4] = this.$t('nav.a6'); |
|||
} else if (order.amount < this.minQty) { |
|||
flag = false; |
|||
this.visibles[orderType].marketAmount = true; |
|||
this.msgList[orderType][4] = this.$t('nav.a7')+` ${this.minQty}`; |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case 3: // stop-limit |
|||
|
|||
if (this.empty(order.trigger_price)) { |
|||
flag = false; |
|||
this.visibles[orderType].trigger = true; |
|||
this.msgList[orderType][0] = this.$t('nav.a8') |
|||
} else if (this.empty(order.entrust_price)) { |
|||
flag = false; |
|||
this.visibles[orderType].limitPrice = true; |
|||
this.msgList[orderType][1] = this.$t('nav.a9') |
|||
} else if (this.empty(order.amount)) { |
|||
flag = false; |
|||
this.visibles[orderType].limitAmount = true; |
|||
this.msgList[orderType][2] = this.$t('nav.b1') |
|||
} |
|||
break; |
|||
|
|||
case 4: // stop-market |
|||
if (this.empty(order.trigger_price)) { |
|||
flag = false; |
|||
this.visibles[orderType].trigger = true; |
|||
this.msgList[orderType][0] = this.$t('nav.b2') |
|||
} else { |
|||
|
|||
if (order.direction == "buy") { |
|||
if (this.empty(total)) { |
|||
flag = false; |
|||
this.visibles[orderType].marketTotal = true; |
|||
this.msgList[orderType][3] = this.$t('nav.b3') |
|||
} else if (total < this.minTotal) { |
|||
flag = false; |
|||
this.visibles[orderType].marketTotal = true; |
|||
this.msgList[orderType][3] = this.$t('nav.b5')+` ${this.minTotal}` |
|||
} |
|||
} |
|||
|
|||
if (order.direction == "sell") { |
|||
if (this.empty(order.amount)) { |
|||
flag = false; |
|||
this.visibles[orderType].marketAmount = true; |
|||
this.msgList[orderType][4] = this.$t('nav.b6') |
|||
} else if (order.amount < this.minQty) { |
|||
flag = false; |
|||
this.visibles[orderType].marketAmount = true; |
|||
this.msgList[orderType][4] =this.$t('nav.b7')+ ` ${this.minQty}` |
|||
} |
|||
} |
|||
|
|||
} |
|||
break; |
|||
} |
|||
|
|||
// 清除所有样式 |
|||
this.clearAll(); |
|||
|
|||
// 返回验证结果 |
|||
return flag; |
|||
}, |
|||
|
|||
}, |
|||
|
|||
created() { |
|||
this.getCoinInfo() |
|||
this.reset(); |
|||
}, |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.buyPercentActive { |
|||
a:link { |
|||
font-weight: bold; |
|||
background: #26a69a; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.sellPercentActive { |
|||
a:link { |
|||
font-weight: bold; |
|||
background: #ef5350; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.input-group-text { |
|||
width: 68px; |
|||
@include flexible(row, center, center); |
|||
} |
|||
|
|||
.market-trade { |
|||
border: none; |
|||
border-color: transparent; |
|||
} |
|||
.market{ |
|||
width: 300px; |
|||
height: 500px; |
|||
overflow: scroll; |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
padding: 10px; |
|||
display: -webkit-box; |
|||
color: black; |
|||
.ov{ |
|||
width: 100%; |
|||
table-layout:fixed; |
|||
word-break:break-all; |
|||
overflow:hidden; |
|||
} |
|||
} |
|||
.p-y-xs{ |
|||
padding: 5px 0; |
|||
} |
|||
.el-button{ |
|||
padding: 0!important; |
|||
background: transparent!important; |
|||
} |
|||
|
|||
.Tab_buy{ |
|||
width: 50%; |
|||
height: 40px; |
|||
color: #ffffff; |
|||
line-height: 40px; |
|||
text-align: center; |
|||
border-radius: 40px; |
|||
background-color: #25a750; |
|||
} |
|||
.Tab_buy1{ |
|||
width: 50%; |
|||
height: 40px; |
|||
color: #ffffff; |
|||
line-height: 40px; |
|||
text-align: center; |
|||
border-radius: 40px; |
|||
} |
|||
.Tab_sell{ |
|||
width: 50%; |
|||
height: 40px; |
|||
color: #ffffff; |
|||
line-height: 40px; |
|||
text-align: center; |
|||
border-radius: 40px; |
|||
background-color: #ca3f64; |
|||
} |
|||
.Tab_sell1{ |
|||
width: 50%; |
|||
height: 40px; |
|||
color: #ffffff; |
|||
line-height: 40px; |
|||
text-align: center; |
|||
border-radius: 40px; |
|||
} |
|||
|
|||
.market-trade input{ |
|||
border: 1px solid #404040; |
|||
} |
|||
.input-group-text{ |
|||
border: 1px solid #404040; |
|||
background-color: #121212; |
|||
} |
|||
.form-control{ |
|||
background-color: #121212; |
|||
} |
|||
.market-trade p{ |
|||
color: #e7e7e7; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,54 @@ |
|||
<template> |
|||
<div class="col-md-3"> |
|||
<div class="market-news mt15"> |
|||
|
|||
<h2 class="heading">{{ $t("common.news") }}</h2> |
|||
<ul> |
|||
<li v-for="item in records" :key="item.id"> |
|||
|
|||
<router-link :to="`/college/detail/${item.category_id}/${item.id}`"> |
|||
<strong>{{ truncate(item.title, 25) }}</strong> |
|||
{{ truncate(item.excerpt, 50) }} |
|||
<span>{{ item.updated_at }}</span> |
|||
</router-link> |
|||
|
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Exchange from "@/api/exchange"; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
records: [], // 所有新动态 |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
getDynamic() { |
|||
Exchange.newTrends(10).then(data => { |
|||
this.records = data; |
|||
}).catch(err => { |
|||
|
|||
}); |
|||
}, |
|||
truncate(str, length) { |
|||
return _.truncate(str, { |
|||
length |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
created() { |
|||
this.getDynamic(); |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
@ -0,0 +1,358 @@ |
|||
<template> |
|||
<div class="col-md-9"> |
|||
<div class="exchange-history order-history market-order mt15"> |
|||
<ul class="nav nav-pills" role="tablist"> |
|||
<li class="nav-item" @click="toggle('stockholdPosition')"> |
|||
<a class="nav-link active" data-toggle="pill" href="#stock-holdPosition" role="tab" aria-selected="true">{{$t("exchange.a2")}}</a> |
|||
</li> |
|||
<li class="nav-item" @click="toggle('opens')"> |
|||
<a class="nav-link" data-toggle="pill" href="#open-orders" role="tab" aria-selected="true"> |
|||
{{ $t("common.open-orders") }}</a> |
|||
</li> |
|||
<!-- <li class="nav-item" @click="toggle('conditions')"> |
|||
<a class="nav-link" data-toggle="pill" href="#stop-orders" role="tab" aria-selected="false"> |
|||
{{ $t("common.condition-orders") }}</a> |
|||
</li> --> |
|||
<li class="nav-item" @click="toggle('histories')"> |
|||
<a class="nav-link" data-toggle="pill" href="#order-history" role="tab" aria-selected="false"> |
|||
{{ $t("common.history-orders") }}</a> |
|||
</li> |
|||
</ul> |
|||
<div class="tab-content"> |
|||
<div class="tab-pane fade show active" id="open-orders" role="tabpanel"> |
|||
<table class="table" v-if="ordersOpen.total"> |
|||
<thead> |
|||
<tr class="text-nowrap"> |
|||
<th>{{ $t("common.created") }}</th> |
|||
<th>{{ $t("common.pair") }}</th> |
|||
<th>{{ $t("common.direction") }}</th> |
|||
<th>{{ $t("common.order-type") }}</th> |
|||
<th>{{ $t("common.order-price") }}</th> |
|||
<th>{{ $t("common.order-amount") }}</th> |
|||
<th>{{ $t("common.executed-amount") }}</th> |
|||
<th>{{ $t("common.executed-total") }}</th> |
|||
<th>{{ $t("common.outstanding") }}</th> |
|||
<th>{{ $t("common.order-total") }}</th> |
|||
<!-- <th>状态</th> --> |
|||
<th class="text-right">{{ $t("common.action") }}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody class="order-tbody"> |
|||
<tr v-for="(item,idx) in ordersOpen.data" :key="item.id" class="text-nowrap"> |
|||
<td>{{item.created_at}}</td> |
|||
<td>{{item.symbol}}</td> |
|||
|
|||
<template v-if="item.entrust_type == 1"> |
|||
<td class="green">{{$t("common.buy-in")}}</td> |
|||
</template> |
|||
<template v-else-if="item.entrust_type == 2"> |
|||
<td class="red">{{$t("common.sell-out")}}</td> |
|||
</template> |
|||
|
|||
<td> |
|||
<template v-if="item.type==1">{{ $t("common.limit-type") }}</template> |
|||
<template v-if="item.type==2">{{ $t("common.market-type") }}</template> |
|||
</td> |
|||
|
|||
<td>{{item.entrust_price|omitTo(priceDecimals)}}</td> |
|||
|
|||
<td>{{item.amount|omitTo(qtyDecimals)}}</td> |
|||
|
|||
<td>{{item.traded_amount|omitTo(priceDecimals)}}</td> |
|||
<td>{{item.traded_money|omitTo(priceDecimals)}}</td> |
|||
|
|||
<td>{{item.surplus_amount||omitTo(qtyDecimals)}}</td> |
|||
|
|||
<td>{{item.money ? Math.omitTo(item.money, priceDecimals) : '-'}}</td> |
|||
<!-- <td>完成</td> --> |
|||
<td class="text-nowrap"> |
|||
<button type="button" class="btn btn-sm btn-outline-danger" |
|||
@click="delOrder(item,idx)">{{ $t("common.cancel") }}</button> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
<div class="no-data" v-else> |
|||
<span> |
|||
<i class="icon ion-md-document"></i> |
|||
{{ $t("common.notData") }} |
|||
</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="tab-pane fade" id="stock-holdPosition" role="tabpanel"> |
|||
<table class="table" v-if="holdPositionList.length>0"> |
|||
<thead> |
|||
<tr class="text-nowrap"> |
|||
<th>{{$t("exchange.a3")}}</th> |
|||
<th>{{$t("exchange.a4")}}</th> |
|||
<th>{{$t("exchange.a5")}}</th> |
|||
<th>{{$t("exchange.a6")}}</th> |
|||
<th>{{$t("exchange.a7")}}</th> |
|||
<th>{{$t("exchange.a8")}}</th> |
|||
<th>{{$t("exchange.a9")}}</th> |
|||
<th>{{$t("exchange.a10")}}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody class="order-tbody"> |
|||
<tr v-for="(item,idx) in holdPositionList" :key="item.id" class="text-nowrap"> |
|||
<td>{{item.pair_name}}</td> |
|||
<td>{{item.market}}</td> |
|||
<td>{{item.usable_balance}}</td> |
|||
<td>{{item.realtimePrice}}</td> |
|||
<td>{{item.cost_price}}</td> |
|||
<td>{{item.dayProfit}}</td> |
|||
<td>{{item.unRealProfit}}</td> |
|||
<td>{{item.profitRate}}</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
<span class="no-data" v-else> |
|||
<i class="icon ion-md-document"></i> |
|||
{{ $t("common.notData") }} |
|||
</span> |
|||
<!-- <div class="p-2"> |
|||
<el-pagination layout="prev, pager, next" :page-size="holdPositionList.per_page" |
|||
@current-change="changePagination1" :total="holdPositionList.total"> |
|||
</el-pagination> |
|||
</div> --> |
|||
</div> |
|||
|
|||
<div class="tab-pane fade" id="stop-orders" role="tabpanel"> |
|||
<!-- <ul class="d-flex justify-content-between market-order-item"> |
|||
<li>{{ $t("common.created") }}</li> |
|||
<li>{{ $t("common.pair") }}</li> |
|||
<li>{{ $t("common.direction") }}</li> |
|||
<li>{{ $t("common.order-type") }}</li> |
|||
<li>{{ $t("common.avg-price") }}</li> |
|||
<li>{{ $t("common.executed") }}</li> |
|||
<li>{{ $t("common.order-total") }}</li> |
|||
<li>{{ $t("common.price-total") }}</li> |
|||
</ul> |
|||
<span class="no-data"> |
|||
<i class="icon ion-md-document"></i> |
|||
{{ $t("common.notData") }} |
|||
</span> --> |
|||
</div> |
|||
|
|||
<div class="tab-pane fade" id="order-history" role="tabpanel"> |
|||
<table class="table" v-if="ordersHistory.total"> |
|||
<thead> |
|||
<tr class="text-nowrap"> |
|||
<th>{{ $t("common.created") }}</th> |
|||
<th>{{ $t("common.pair") }}</th> |
|||
<th>{{ $t("common.direction") }}</th> |
|||
<th>{{ $t("common.order-type") }}</th> |
|||
<th>{{ $t("common.order-price") }}</th> |
|||
<th>{{ $t("common.order-amount") }}</th> |
|||
<th>{{ $t("common.executed-amount") }}</th> |
|||
<th>{{ $t("common.avg-price") }}</th> |
|||
|
|||
<th>{{ $t("common.order-total") }}</th> |
|||
<th>{{ $t("common.status") }}</th> |
|||
<th>{{ $t("common.details") }}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<template v-for="(item,index) in ordersHistory.data"> |
|||
<tr class="text-nowrap" :key="item.id" :name="item.entrust_type+'_'+item.id+'_'+index" |
|||
slot="title" @click="handleChange(item)"> |
|||
<td>{{item.created_at}}</td> |
|||
<td>{{item.symbol}}</td> |
|||
<template v-if="item.entrust_type == 1"> |
|||
<td class="green">{{$t("common.buy-in")}}</td> |
|||
</template> |
|||
<template v-else-if="item.entrust_type == 2"> |
|||
<td class="red">{{$t("common.sell-out")}}</td> |
|||
</template> |
|||
<td> |
|||
<template v-if="item.type==1">{{ $t("common.limit-type") }}</template> |
|||
<template v-if="item.type==2">{{ $t("common.market-type") }}</template> |
|||
</td> |
|||
<td>{{item.entrust_price ? Math.omitTo(item.entrust_price, priceDecimals) : '-'}} |
|||
</td> |
|||
<td>{{item.amount|omitTo(qtyDecimals)}}</td> |
|||
|
|||
<td>{{ item.traded_amount|omitTo(qtyDecimals) }}</td> |
|||
|
|||
<td v-if="item.status"> |
|||
{{Math.division(item.traded_money,item.traded_amount,priceDecimals)}} |
|||
</td> |
|||
<td v-else>-</td> |
|||
<td>{{item.traded_money|omitTo(priceDecimals)}}</td> |
|||
<template v-if="item.status"> |
|||
<td v-if="item.status == 3"> |
|||
{{ $t("common.completed") }} |
|||
</td> |
|||
</template> |
|||
<template v-else> |
|||
<td>{{ $t("common.canceled") }}</td> |
|||
</template> |
|||
<td> |
|||
<i v-if="item.show" class="el-icon-arrow-down"></i> |
|||
<i v-else class="el-icon-arrow-right"></i> |
|||
</td> |
|||
</tr> |
|||
<tr :key="item.id+'t'" v-if="item.show"> |
|||
<td colspan="11"> |
|||
<template v-if="item.children && item.children.length"> |
|||
<div v-for="rec in item.children" :key="rec.order_id"> |
|||
<span> |
|||
<span class="text-secondary"> |
|||
{{ $t("common.id") }} |
|||
</span>:{{ rec.order_id}}   |
|||
</span> |
|||
<span><span class="text-secondary"> |
|||
{{ $t("common.created") }}</span>:{{ rec.created_at}} |
|||
 </span> |
|||
<span><span class="text-secondary"> |
|||
{{ $t("common.filled-price") }}</span>:{{ rec.unit_price|omitTo(priceDecimals)}} |
|||
 </span> |
|||
<span><span class="text-secondary"> |
|||
{{ $t("common.filled-amount") }}</span>:{{ rec.trade_amount|omitTo(qtyDecimals)}} |
|||
 </span> |
|||
<span><span class="text-secondary"> |
|||
{{ $t("common.filled-total") }}</span>:{{ rec.trade_money|omitTo(priceDecimals)}} |
|||
 </span> |
|||
<span><span class="text-secondary"> |
|||
{{ $t("common.fee") }}</span>:{{ rec.trade_fee|omitTo(priceDecimals)}} |
|||
 </span> |
|||
<span v-if="rec.total_profit>=0"><span class="text-secondary"> |
|||
{{ $t("common.d23") }}</span>:{{rec.total_profit}} |
|||
 </span> |
|||
</div> |
|||
</template> |
|||
<div class="text-center text-secondary" v-else> |
|||
Loading... |
|||
</div> |
|||
</td> |
|||
</tr> |
|||
</template> |
|||
</tbody> |
|||
</table> |
|||
<span class="no-data" v-else> |
|||
<i class="icon ion-md-document"></i> |
|||
{{ $t("common.notData") }} |
|||
</span> |
|||
<div class="p-2"> |
|||
<el-pagination layout="prev, pager, next" :page-size="ordersHistory.per_page" |
|||
@current-change="changePagination" :total="ordersHistory.total"> |
|||
</el-pagination> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Order from "@/api/order"; |
|||
import math from "@/utils/class/math.js"; |
|||
|
|||
export default { |
|||
props: [ |
|||
"ordersOpen", |
|||
"ordersHistory", |
|||
"holdPositionList", |
|||
"conditionOrders", |
|||
"priceDecimals", |
|||
"qtyDecimals", |
|||
"isLogin", |
|||
"pair" |
|||
], |
|||
|
|||
data() { |
|||
return { |
|||
currentTab: "stockholdPosition", |
|||
activeItems: [] |
|||
}; |
|||
}, |
|||
methods: { |
|||
omitTo: math.omitTo, |
|||
toggle(name) { |
|||
this.currentTab = name; |
|||
// 更新父组件 |
|||
this.$emit("change", name); |
|||
}, |
|||
|
|||
update() { |
|||
this.$emit("update"); |
|||
}, |
|||
|
|||
// 撤销当前订单 |
|||
delOrder(item, idx) { |
|||
let data = { |
|||
entrust_id: item.id, |
|||
entrust_type: item.entrust_type, |
|||
symbol: item.symbol |
|||
}; |
|||
this.$confirm(this.$t("order.ifCancel", { |
|||
confirmButtonText: this.$t('common.confirmBtn'), |
|||
cancelButtonText: this.$t('common.cancelBtn'), |
|||
})) |
|||
.then(res => { |
|||
Order.stockcancelEntrust(data) |
|||
.then(res => { |
|||
this.$message.success(this.$t("order.cancelSuccess")); |
|||
this.update(); |
|||
}) |
|||
.catch(() => {}); |
|||
}) |
|||
.catch(err => {}); |
|||
}, |
|||
|
|||
handleChange(item) { |
|||
this.$set(item, "show", !item.show); |
|||
if (item.children) return; |
|||
if (this.isLogin) { |
|||
Order.getstockEntrustTradeRecord({ |
|||
entrust_type: item.entrust_type, |
|||
entrust_id: item.id |
|||
}) |
|||
.then(data => { |
|||
this.$set(item, "children", data); |
|||
}) |
|||
.catch(err => {}); |
|||
} |
|||
|
|||
}, |
|||
changePagination(idx) { |
|||
// console.log(idx); |
|||
this.$emit("Pagination", idx); |
|||
}, |
|||
changePagination1(idx) { |
|||
// console.log(idx); |
|||
this.$emit("Pagination1", idx); |
|||
} |
|||
}, |
|||
created() { |
|||
// console.log(this.ordersHistory); |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.market-order{ |
|||
border: 1px solid #404040; |
|||
} |
|||
</style> |
|||
<style lang="scss"> |
|||
.exchange-history { |
|||
.nav { |
|||
background: #121212; |
|||
} |
|||
|
|||
.nav-link.active { |
|||
color: #007bff; |
|||
background: transparent; |
|||
} |
|||
} |
|||
|
|||
.order-tbody { |
|||
display: table-row-group !important; |
|||
|
|||
tr { |
|||
float: none; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,260 @@ |
|||
<template> |
|||
<div class="col-md-3"> |
|||
<div class="market-pairs"> |
|||
|
|||
<!-- 搜索框区域 --> |
|||
<div class="input-group"> |
|||
<div class="input-group-prepend"> |
|||
<span class="input-group-text" id="inputGroup-sizing-sm"> |
|||
<i class="icon ion-md-search"></i> |
|||
</span> |
|||
</div> |
|||
<input type="text" class="form-control" v-model="keyword" :placeholder="$t('exchange.search')" aria-describedby="inputGroup-sizing-sm"> |
|||
</div> |
|||
|
|||
<!-- 币种列表 --> |
|||
<ul class="nav nav-pills" role="tablist" ref="navList"> |
|||
|
|||
<li class="nav-item" v-for="(coin, index) in markets" :key="index"> |
|||
<a :class="[`nav-link`, {active:currentCoinIdx == index}]" href @click.prevent="currentCoinIdx = index"> |
|||
<!-- 查看收藏交易对 --> |
|||
<template v-if="coin.coin_name == 'fav'"> |
|||
<i class="icon ion-md-star"></i> |
|||
</template> |
|||
<template v-else> |
|||
{{ coin.coin_name }} |
|||
</template> |
|||
</a> |
|||
</li> |
|||
|
|||
</ul> |
|||
|
|||
<!-- 币种行情信息,不同的交易对价值 --> |
|||
<div class="tab-content"> |
|||
|
|||
<div v-for="(coin, index) in markets" :key="index"> |
|||
<table class="table" v-if="currentCoinIdx == index"> |
|||
<thead> |
|||
<tr style="display:block"> |
|||
<th class="w-33">{{ $t("exchange.pair") }}</th> |
|||
<th class="w-33 text-right">{{ $t("exchange.last-price") }}</th> |
|||
<th class="w-33 text-right">{{ $t("exchange.change") }}</th> |
|||
</tr> |
|||
</thead> |
|||
|
|||
<tbody> |
|||
<tr style="display:block" :class="{highlight : item.pair_id == marketId}" @click="$emit('update:symbol', item.symbol)" v-for="(item, key) in coin.marketInfoList" :key="key" v-show="isShow(item)"> |
|||
<td class="w-33" style="white-space:nowrap"> |
|||
<i class="icon ion-md-star h6" :class="{ active: isCoolect(item) }" @click.self="handleFav(item)"></i> |
|||
{{ coin.coin_name == 'fav' ? item.pair_name : item.coin_name + '/' + coin.coin_name}} |
|||
</td> |
|||
<td class="w-33 text-right"> |
|||
{{ coin.coin_name == 'fav' ? item.close : item.price}} |
|||
</td> |
|||
<!-- <td class="w-33 text-right" :class="item.increaseStr.startsWith('-') ? 'red' : 'green'"> --> |
|||
<td class="w-33 text-right" :class="increaseStrColor(item)"> |
|||
{{ item.increaseStr }} |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Market from "@/api/market"; |
|||
import Home from "@/api/home"; |
|||
import { symbolCircle } from "d3"; |
|||
export default { |
|||
|
|||
props: { |
|||
marketList: { |
|||
type: Array, |
|||
|
|||
// 初始化填充页面排版的数据 |
|||
default: Array(5).fill({ |
|||
coin_name: "-", |
|||
marketInfoList: Array(10).fill({ |
|||
coin_name: "-", |
|||
price: "-", |
|||
increace: 0, |
|||
increaseStr: "+0.00%", |
|||
}), |
|||
}) |
|||
}, |
|||
|
|||
isLogin: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
|
|||
marketId: { |
|||
type: Number, |
|||
default: null, |
|||
}, |
|||
|
|||
firstEnter: true, |
|||
}, |
|||
|
|||
data() { |
|||
return { |
|||
keyword: '', // 搜索关键字 |
|||
current: "fav", |
|||
// 个人收藏的交易对信息 |
|||
favList: { |
|||
coinName: "fav", |
|||
marketInfoList: Array(10).fill({ |
|||
pair: "-", |
|||
price: "-", |
|||
increace: 0, |
|||
increaseStr: "+0.00%", |
|||
}), |
|||
}, |
|||
|
|||
// 收藏交易对的数据结构 |
|||
favList: { |
|||
coin_name: "fav", |
|||
image: require("@/assets/img/waiting.png"), |
|||
marketInfoList: [], |
|||
}, |
|||
|
|||
currentCoinIdx: 0, // 当前展示的币种 |
|||
|
|||
cacheMarketList : [], |
|||
collect:[] |
|||
} |
|||
|
|||
}, |
|||
|
|||
computed: { |
|||
|
|||
markets() { |
|||
// 将行情列表和收藏交易对整理一起 方便渲染 |
|||
return [...this.cacheMarketList, this.favList]; |
|||
}, |
|||
|
|||
}, |
|||
|
|||
watch : { |
|||
marketList (list) { |
|||
if (list.length) this.cacheMarketList = list; |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
|
|||
/* |
|||
* 询问用户是否登录 |
|||
* 如果已经登录 返回true |
|||
* 如果没有登录 询问用户是否登录进行操作 |
|||
*/ |
|||
inquiryLogin() { |
|||
if (!this.isLogin) { |
|||
this.$confirm(this.$t('nav.login'), this.$t('nav.tips'), { |
|||
confirmButtonText: this.$t('home.Login'), |
|||
cancelButtonText: this.$t('home.Cancel'), |
|||
type: "info", |
|||
}) |
|||
.then(() => { |
|||
location.href = "/login"; |
|||
}) |
|||
.catch(() => {}); |
|||
return false; |
|||
} else { |
|||
return true; |
|||
} |
|||
}, |
|||
|
|||
// 搜索关键字 |
|||
isShow(symbol) { |
|||
|
|||
const reg = new RegExp(this.keyword, "gi"); |
|||
if (!this.keyword) return true; |
|||
else { |
|||
let name = symbol.pair_name || symbol.coin_name; |
|||
return name.search(reg) >= 0; |
|||
} |
|||
// return !this.keyword || symbol.coinName.concat(symbol.pair).search(reg) >= 0; |
|||
|
|||
}, |
|||
// 是否为自选 |
|||
isCoolect(i) { |
|||
// console.log(this.markets[this.currentCoinIdx].marketInfoList) |
|||
return this.favList.marketInfoList.map((item) => item.pair_name).includes(i.pair_name); |
|||
}, |
|||
// 添加收藏的方法 |
|||
handleFav(item) { |
|||
let data = { |
|||
pair_name: item.pair_name, |
|||
}; |
|||
Home.option(data) |
|||
.then((res) => { |
|||
this.getCollect(); |
|||
if (res) { |
|||
this.$message.success(this.$t("home.add")); |
|||
} else { |
|||
this.$message.success(this.$t("home.cancel")); |
|||
} |
|||
}) |
|||
.catch((err) => {}); |
|||
}, |
|||
getCollect() { |
|||
Home.getCollect() |
|||
.then((res) => { |
|||
this.favList.marketInfoList = res||[]; |
|||
}) |
|||
.catch((err) => {}); |
|||
}, |
|||
increaseStrColor(item){ |
|||
if (item && item.increaseStr && item.increaseStr.startsWith('-')) { |
|||
return 'red' |
|||
} else { |
|||
return 'green' |
|||
} |
|||
} |
|||
}, |
|||
|
|||
created() { |
|||
|
|||
}, |
|||
|
|||
mounted() { |
|||
// 初始化市场行情 覆盖初始化的[] |
|||
Market.getstockMarketList().then(data => { |
|||
this.cacheMarketList = data; |
|||
}).catch(err => {}); |
|||
|
|||
// console.log(this.isLogin) |
|||
// 如果已登陆 则写入收藏交易对 |
|||
if (this.isLogin) { |
|||
this.getCollect() |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.highlight { |
|||
background: #f6f8f9; |
|||
background: #F8F8FF; |
|||
|
|||
td:first-child { |
|||
color: #007bff !important; |
|||
} |
|||
} |
|||
|
|||
.w-33 { |
|||
display: inline-block !important; |
|||
vertical-align: top !important; |
|||
width: 32% !important; |
|||
} |
|||
.active{ |
|||
color: #326AEB!important; |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue