<template>
|
<div class="kline-charts">
|
<ul class="flex px-32 pb-20 box-border justify-between text-grey fontStyle font-26"
|
style="border-bottom:1px solid rgba(68,75,88,0.2);display: flex;justify-content: space-between; ">
|
<li v-for="item in timeList" :key="item.id" class="mr-10" :class="{ 'textColor': item.id === timeValue.id }"
|
@click="choiceTime(item)">{{ item.text }}</li>
|
</ul>
|
<div class="relative">
|
<!-- <div style="width: 100%;height: 414px;" v-show="chartLoading==false" -->
|
<div style="width: 100%;height: 414px;">
|
<div id="kline" class="h-828" style="height: 414px;width: 350px;"></div>
|
</div>
|
</div>
|
<!-- <view v-if="chartLoading==true" style="margin-top: 180px;margin-left: 45%;">
|
<van-loading color="#1989fa" size="40px" />
|
</view> -->
|
<!-- <view>
|
<u-loadmore
|
:status="loadStatus"
|
:bottom="40"
|
:offset="100"
|
/>
|
</view> -->
|
<!-- <ul class="flex px-32 py-20 box-border justify-between text-grey font-26" v-if="showBottom"
|
style="border-top:1px solid rgba(68,75,88,0.2);display: flex;justify-content: space-between;">
|
<li v-for="item in subTechnicalIndicatorTypes" :key="item" class="mr-20"
|
:class="{ 'textColor': typeValue === item }" @click="choiceType(item)">{{ item }}</li>
|
</ul> -->
|
</div>
|
</template>
|
<script>
|
import {
|
init,
|
dispose
|
} from 'klinecharts'
|
let chart = null
|
// import app from "app.js";
|
// import { _getKline } from '@/api/market.js'
|
// import ws from "../../static/chart_main/ws.js"
|
import config from './config'
|
import {
|
mapState
|
} from "vuex";
|
import Option from '@/api/option'
|
// import Loading from ''
|
// import { Loading } from '@vant/weapp';
|
// import {Loading} from '../../node_modules/@vant/weapp'
|
// import { Loading } from '../../node_modules/@vant'
|
// import { Loading } from '@/@vant/weapp'
|
// import { Loading } from '@vant/weapp';
|
// @import '@vant/weapp/dist/index.css';
|
// import 'vant/lib/index.css'; // 引入样式文件
|
|
import {
|
scientificNotationToString
|
} from "@/utils/utis";
|
export default {
|
name: 'KlineCharts',
|
data() {
|
return {
|
symbol: '',
|
dealInfo: {}, //交易对信息
|
ws: null,
|
chartLoading: true,
|
isshow: true,
|
timeValue: {}, // 当前k线周期
|
subTechnicalIndicatorTypes: ['MA', 'EMA', 'BOLL', 'VOL', 'MACD', 'KDJ', 'RSI'],
|
typeValue: 'VOL', //图形类型
|
klinecharts: null, //K线图实例
|
tvBars: [],
|
chart: null,
|
// resolution: '1',//分辨率
|
// lang: 'en', //语言
|
paneId: '',
|
chartData: [], // 图表数据
|
timer: null,
|
newwebsockt: null
|
}
|
},
|
created() {
|
|
},
|
computed: {
|
...mapState({
|
socket: "ws",
|
socket1: "ws1",
|
theme: 'theme'
|
}),
|
timeList() {
|
return [{
|
id: '1min',
|
time: '1min',
|
// text: this.$t('分时'),
|
text: 'TS',
|
ts: 1 * 60 * 1000
|
},
|
// {
|
// id: '3min',
|
// time: '3min',
|
// // text: this.$t('分时'),
|
// text: '3min',
|
// ts: 3 * 60 * 1000
|
// },
|
// {
|
// id: '5min',
|
// time: '5min',
|
// // text: this.$t('分时'),
|
// text: '5min',
|
// ts: 5 * 60 * 1000
|
// },
|
{
|
id: '15min',
|
time: '15min',
|
// text: '15' + this.$t('分'),
|
text: '15min',
|
ts: 15 * 60 * 1000
|
},
|
{
|
id: '30min',
|
time: '30min',
|
// text: '30' + this.$t('分'),
|
text: '30min',
|
ts: 30 * 60 * 1000
|
},
|
{
|
id: '60min',
|
time: '60min',
|
// text: '1' + this.$t('小时'),
|
text: '1hour',
|
ts: 60 * 60 * 1000
|
},
|
// {
|
// id: '4hour',
|
// time: '4hour',
|
// // text: '4' + this.$t('小时'),
|
// text: '4hour',
|
// ts: 4 * 60 * 60 * 1000
|
// },
|
{
|
id: '1day',
|
time: '1day',
|
// text: '1' + this.$t('天'),
|
text: '1day',
|
ts: 24 * 60 * 60 * 1000
|
},
|
{
|
id: '1week',
|
time: '1week',
|
// text: '1' + this.$t('周'),
|
text: '1week',
|
ts: 7 * 24 * 60 * 60 * 1000
|
},
|
{
|
id: '1mon',
|
time: '1mon',
|
// text: '1' + this.$t('月'),
|
text: '1mon',
|
ts: 30 * 24 * 60 * 60 * 1000
|
}
|
] //时间列表
|
}
|
},
|
components: {
|
// 'van-loading': Loading // 引入 van-loading 组件
|
},
|
props: {
|
contract: {
|
type: String,
|
default: ''
|
},
|
symbol: {
|
type: String,
|
default: ''
|
},
|
updateKey: {
|
type: Number,
|
default: 0
|
},
|
updateData: {
|
type: Object,
|
default () {
|
return {}
|
}
|
},
|
showBottom: {
|
type: Boolean,
|
default: true
|
},
|
isChangeLine: {
|
type: Boolean,
|
default: false
|
},
|
},
|
mounted() {
|
this.chart = null
|
this.initData()
|
|
},
|
beforeDestroy() {
|
this.clearTimer()
|
dispose('kline')
|
},
|
watch: {
|
isChangeLine(val) {
|
this.fetchData()
|
},
|
|
updateKey() { // 更新charts
|
this.$nextTick(() => {
|
this.setChartType()
|
chart.updateData(newData)
|
})
|
}
|
},
|
methods: {
|
initData() {
|
this.timeValue = this.timeList[0]
|
chart = init('kline', config);
|
chart.setOffsetRightSpace(25)
|
chart.setDataSpace(10)
|
chart.setPriceVolumePrecision(4, 2)
|
chart.createTechnicalIndicator('MA', false, {id: 'candle_pane'});
|
this.paneId = chart.createTechnicalIndicator('VOL');
|
this.fetchData()
|
},
|
upsertTvBars(isFirstCall, bars) {
|
if (isFirstCall) {
|
this.tvBars = [];
|
}
|
bars.forEach((bar) => {
|
if (this.tvBars.length === 0 || !this.tvBars.find(o => o.time === bar.time)) {
|
this.tvBars.push(bar);
|
}
|
})
|
this.tvBars.sort((a, b) => {
|
if (a.time < b.time) return -1;
|
if (a.time > b.time) return 1;
|
return 0;
|
});
|
},
|
sub() {
|
console.log('msg1111', this.msg)
|
this.Ws.send({
|
cmd: "sub",
|
msg: this.msg
|
})
|
},
|
// 生成订阅数据
|
createMsg() {
|
if (this.contract) {
|
return `swapKline_${this.symbolName}_${this.resolution(this.interval)}`
|
} else {
|
|
return `Kline_${this.getSymbol(this.symbolName)}_${this.resolution(this.interval)}`
|
}
|
},
|
unzip(b64Data) {
|
let u8 = atob(b64Data)
|
let jiya = 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
|
|
},
|
getMap(data) {
|
return {
|
time: data.id * 1000,
|
close: data.close * 1,
|
open: data.open * 1,
|
high: data.high * 1,
|
low: data.low * 1,
|
volume: data.vol * 1,
|
};
|
},
|
fetchData(time) { // 获取数据
|
let data = {
|
symbol: this.symbol.replace("/", "").toLowerCase(),
|
period: time ? this.resolution(time.id) : '1min',
|
size: 200,
|
zip: 0
|
}
|
Option.getKline(data)
|
.then((res) => {
|
this.tvBars = res.data.data
|
chart.setPriceVolumePrecision(5, 5)
|
this.setChartType()
|
let str = scientificNotationToString(this.tvBars[this.tvBars.length-1].close.toString())
|
let length = 2
|
if (str.indexOf('.') != -1) {
|
length = str.split('.')[1].length
|
}
|
chart.setPriceVolumePrecision(length, 2)
|
this.$emit('tvBars', this.tvBars[this.tvBars.length-1])
|
chart.applyNewData(this.tvBars);
|
setTimeout(() => {
|
this.chartLoading = false
|
}, 1000)
|
})
|
.catch(() => {});
|
if (this.newwebsockt) {
|
this.newwebsockt.close()
|
}
|
if (this.contract == 1) {
|
this.newwebsockt = new WebSocket('wss://deal.kcscoinmarket.com/ws2');
|
} else {
|
this.newwebsockt = new WebSocket('wss://deal.kcscoinmarket.com/ws1');
|
}
|
|
// 监听 WebSocket 连接打开事件
|
this.newwebsockt.addEventListener('open', (event) => {
|
// console.log('WebSocket连接已建立:', event);
|
// 发送连接后需要发送的消息
|
if (this.contract == "1") {
|
|
if (time) {
|
this.newwebsockt.send(JSON.stringify({
|
"cmd": "sub",
|
"msg": "swapKline" + "_" + this.symbol.slice(0, -5).toUpperCase()+'_'+time.id
|
}));
|
} else {
|
this.newwebsockt.send(JSON.stringify({
|
"cmd": "sub",
|
"msg": "swapKline_" + this.symbol.slice(0, -5).toUpperCase() +
|
"_1min",
|
}));
|
}
|
} else {
|
if (time) {
|
this.newwebsockt.send(JSON.stringify({
|
"cmd": "sub",
|
"msg": "Kline" + "_" + this.symbol.toLowerCase().replace('/', '') +
|
"_" +
|
time.id
|
}));
|
} else {
|
this.newwebsockt.send(JSON.stringify({
|
"cmd": "sub",
|
"msg": "Kline_" + this.symbol.toLowerCase().replace('/', '') + "_1min",
|
}));
|
}
|
}
|
|
|
});
|
|
// 监听 WebSocket 消息事件
|
this.newwebsockt.addEventListener('message', (event) => {
|
// console.log('接收到的消息:', event.data);
|
// 处理从 WebSocket 服务器接收到的数据
|
const message = JSON.parse(event.data);
|
if (message.type === 'dynamic' && message.data) {
|
//科学计数法时转成字符串
|
let str = scientificNotationToString(message.data.close.toString())
|
let length = 2
|
if (str.indexOf('.') != -1) {
|
length = str.split('.')[1].length
|
}
|
chart.setPriceVolumePrecision(length, 2)
|
this.setChartType()
|
this.$emit('tvBars', message.data)
|
if (message.data.id != this.tvBars[this.tvBars.length - 1].id) {
|
this.tvBars = [...this.tvBars, message.data];
|
chart.applyNewData(this.tvBars);
|
this.$emit('updataLine', false)
|
} else {
|
const timestampInSeconds = Math.floor(Date.now() / 1000);
|
message.data.timestamp = message.data.id * 1000
|
message.data.volume = message.data.vol
|
this.tvBars[this.tvBars.length - 1] = message.data;
|
chart.applyNewData(this.tvBars);
|
this.$emit('updataLine', false)
|
}
|
|
// }
|
|
} else if (message.type === 'ping') {
|
this.newwebsockt.send(JSON.stringify({
|
"cmd": "pong",
|
}));
|
}
|
});
|
|
// 监听 WebSocket 连接关闭事件
|
this.newwebsockt.addEventListener('close', (event) => {
|
console.log('WebSocket连接已关闭:', event);
|
});
|
|
// 监听 WebSocket 错误事件
|
this.newwebsockt.addEventListener('error', (event) => {
|
console.log('WebSocket发生错误:', event);
|
});
|
|
},
|
resolution(resolution) {
|
let T = "";
|
if (isNaN(resolution * 1)) {
|
T = resolution
|
.replace("D", "day")
|
.replace("W", "week")
|
.replace("M", "mon");
|
} else {
|
if (resolution > 60) {
|
T = Math.floor(resolution / 60) + "hour";
|
} else {
|
T = resolution + "min";
|
}
|
}
|
return T;
|
},
|
setChartType() {
|
|
let type = 'area'
|
if (this.timeValue.id === '1min') {
|
type = 'area'
|
} else {
|
type = 'candle_solid'
|
}
|
chart.setStyleOptions({
|
candle: {
|
type
|
}
|
})
|
},
|
choiceTime(value) { // 选择周期
|
this.chartLoading = true
|
this.timeValue = value
|
this.chart = null
|
this.fetchData(value)
|
},
|
choiceType(type) { // 选择副线
|
this.typeValue = type
|
chart.createTechnicalIndicator(type, false, {
|
id: this.paneId
|
})
|
},
|
clearTimer() {
|
clearInterval(this.timer)
|
this.timer = null
|
},
|
},
|
deactivated() {
|
this.clearTimer()
|
}
|
}
|
</script>
|
<style lang="scss" scoped>
|
.textColor {
|
color: #000000;
|
font-weight: bold;
|
}
|
|
// #kline {
|
// // min-height: 828px;
|
// height: 1200px;
|
// }
|
</style>
|