<template>
<div class="k-chart">
<div class="legend">
<div class="title">
<span class="titleLegend"></span>
{
{title}}
</div>
<div class="MA">
<span class="MA5Figure"></span>
MA5: {
{MA5LegendData}}
</div>
<div class="MA MA10">
<span class="MA5Figure MA10Figure"></span>
MA10: {
{MA10LegendData}}
</div>
<div class="MA MA20">
<span class="MA5Figure MA20Figure"></span>
MA20: {
{MA20LegendData}}
</div>
<div class="MA MA30">
<span class="MA5Figure MA30Figure"></span>
MA30: {
{MA30LegendData}}
</div>
</div>
<div class="chart" id="kChart"></div>
</div>
</template>
<script>
import * as echarts from 'echarts/index';
import { round } from "../../../unit/utils";
let myChart = null;
const userZoom = {
change: false,
start: 0,
end: 0,
};
let pointerInChart = false;
function calculateMA(dayCount, data) {
let result = [];
for (let i = 0, len = data.length; i < len; i++) {
if (i < dayCount) {
result.push('-');
continue;
}
let sum = 0;
for (let j = 0; j < dayCount; j++) {
sum += Number(data[i - j][1]);
}
result.push(+(sum / dayCount).toFixed(2));
}
return result;
}
export default {
name: "KChart",
props: {
title: {
type: String,
},
chartType: {
type: String,
default: () => 'L',
},
subjectType: {
type: String,
},
isShow: {
type: Boolean,
},
xData: {
type: Array,
default: () => [],
},
kData: {
type: Array,
default: () => [],
},
amountData: {
type: Array,
default: () => [],
},
rateData: {
type: Array,
default: () => [],
},
loading: {
type: Boolean,
},
active: {
type: String
},
},
mounted() {
window.addEventListener('resize', this.resize);
myChart = echarts.init(document.getElementById('kChart'));
myChart.on('datazoom', e => {
userZoom.change = true;
if (e.batch) {
userZoom.start = e.batch[0].start;
userZoom.end = e.batch[0].end;
} else {
userZoom.start = e.start;
userZoom.end = e.end;
}
});
//鼠标移出坐标系,图例中MA数据显示最后一条
myChart.on('globalout', e => {
this.MA5LegendData = this.ma5[this.ma5.length - 1];
this.MA10LegendData = this.ma10[this.ma10.length - 1];
this.MA20LegendData = this.ma20[this.ma20.length - 1];
this.MA30LegendData = this.ma30[this.ma30.length - 1];
})
},
beforeDestroy() {
myChart.clear();
window.removeEventListener('resize', this.resize);
},
data() {
return {
MA5LegendData: '',
MA10LegendData: '',
MA20LegendData: '',
MA30LegendData: '',
};
},
watch: {
isShow(val) {
if (val && myChart) {
this.resize();
}
},
loading(val) {
if (val) {
this.showLoading();
} else {
myChart.hideLoading();
}
},
options() {
myChart && myChart.setOption(this.options);
},
chartType() {
userZoom.change = false;
},
active() {
if(this.active === 'realtime') {
setTimeout(()=>{
this.resize();
},0)
}
},
ma5(val) {
if (val.length) {
this.MA5LegendData = val[val.length - 1];
} else {
return '';
}
},
ma10(val) {
if (val.length) {
this.MA10LegendData = val[val.length - 1];
} else {
return '';
}
},
ma20(val) {
if (val.length) {
this.MA20LegendData = val[val.length - 1];
} else {
return '';
}
},
ma30(val) {
if (val.length) {
this.MA30LegendData = val[val.length - 1];
} else {
return '';
}
},
},
computed: {
ma5() {
return calculateMA(5, this.kData);
},
ma10() {
return calculateMA(10, this.kData);
},
ma20() {
return calculateMA(20, this.kData);
},
ma30() {
return calculateMA(30, this.kData);
},
options() {
const len = this.xData.length;
let startValue = 0;
let endValue = len - 1;
const dataZoom = [
{
type: 'inside',
xAxisIndex: [0, 1],
textStyle: {
color: 'rgba(255, 255, 255, .8)',
}
},
{
show: true,
xAxisIndex: [0, 1],
type: 'slider',
top: '91%',
textStyle: {
color: 'rgba(255, 255, 255, .8)',
}
}
];
if (this.chartType === 'day' || this.chartType === 'week') {
startValue = len - 60 > 0 ? len - 60 : 0;
}
dataZoom.forEach(item => {
item.startValue = startValue;
item.endValue = endValue;
});
if (userZoom.change) {
let start = userZoom.start;
let end = userZoom.end;
dataZoom.forEach(item => {
item.start = start;
item.end = end;
});
}
const fontColor = 'rgba(255, 255, 255, .95)';
const tooltipBackgroundcolor = '#334163';
const lineColor = '#3A4560';
const Ygreen = '#4CCEAC';
const Yred = '#DB504A';
return {
animation: false,
legend: {
show: false,
left: 'center',
data: [this.title, 'MA5', 'MA10', 'MA20', 'MA30'],
formatter: (params) => {
switch (params) {
case this.title:
return params;
break;
case 'MA5':
//做此判断是为了避免图缩放过程中MA5出现undefined,当值为undefined,就显示-
if(this.MA5LegendData) {
return params + ': ' + this.MA5LegendData;
} else { return params + ': ' + '-'; };
break;
case 'MA10':
if(this.MA10LegendData) {
return params + ': ' + this.MA10LegendData;
} else { return params + ': ' + '-'; }
break;
case 'MA20':
if(this.MA20LegendData) {
return params + ': ' + this.MA20LegendData;
} else { return params + ': ' + '-'; }
break;
case 'MA30':
if(this.MA30LegendData) {
return params + ': ' + this.MA30LegendData;
} else { return params + ': ' + '-'; }
break;
default:
return '';
}
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
borderWidth: 1,
backgroundColor: tooltipBackgroundcolor,
borderColor: lineColor,
padding: 10,
textStyle: {
color: fontColor
},
position: function (pos, params, el, elRect, size) {
let obj = {top: 25};
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = size.viewSize[0] * 0.1;
return obj;
},
formatter: param => {
let ret = [];
ret.push(param[0].name + '<br />');
const amountParam = param.find(item => item.seriesName === '成交量');
if (!amountParam) { return; }
let amount = amountParam.data[1];
amount = this.setZero((Number(amount).toLocaleString('en-US')));
const kParam = param.find(item => item.seriesType === 'candlestick');
if (!kParam) { return; }
// const MA5Param = param.find(item => item.seriesName === 'MA5');
// const MA10Param = param.find(item => item.seriesName === 'MA10');
// const MA20Param = param.find(item => item.seriesName === 'MA20');
// const MA30Param = param.find(item => item.seriesName === 'MA30');
const color = Number(kParam.data.value[2]) > Number(kParam.data.value[1]) ? '#ec0000' : '#00da3c';
const rate = kParam.data.rate.split(',');
const colorRate = Number(rate[0]) > 0 ? '#ec0000' : '#00da3c';
let rateFront = this.setZero((Number(round(rate[0], 2)).toLocaleString('en-US')));
let rateBack = this.setZero((Number(round(rate[1], 2)).toLocaleString('en-US')));
let openData = this.setZero((Number(kParam.data.value[1]).toLocaleString('en-US')));
let highestData = this.setZero((Number(kParam.data.value[4]).toLocaleString('en-US')));
let lowestData = this.setZero((Number(kParam.data.value[3]).toLocaleString('en-US')));
let closeData = this.setZero((Number(kParam.data.value[2]).toLocaleString('en-US')));
ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
<div style="display: flex; align-items: center; margin-right: 20px;">
<div style="width: 10px; height: 10px; border-radius: 50%; background: ${color}; margin-right: 5px;"></div>
<span>开盘</span>
</div>
<span style="font-weight: 600;">${openData}</span>
</div>`);
ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
<div style="display: flex; align-items: center; margin-right: 20px;">
<div style="width: 10px; height: 10px; border-radius: 50%; background: ${color}; margin-right: 5px;"></div>
<span>最高</span>
</div>
<span style="font-weight: 600;">${highestData}</span>
</div>`);
ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
<div style="display: flex; align-items: center; margin-right: 20px;">
<div style="width: 10px; height: 10px; border-radius: 50%; background: ${color}; margin-right: 5px;"></div>
<span>最低</span>
</div>
<span style="font-weight: 600;">${lowestData}</span>
</div>`);
ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
<div style="display: flex; align-items: center; margin-right: 20px;">
<div style="width: 10px; height: 10px; border-radius: 50%; background: ${color}; margin-right: 5px;"></div>
<span>收盘</span>
</div>
<span style="font-weight: 600;">${closeData}</span>
</div>`);
ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
<div style="display: flex; align-items: center; margin-right: 20px;">
<div style="width: 10px; height: 10px; border-radius: 50%; background: ${colorRate}; margin-right: 5px;"></div>
<span>涨跌</span>
</div>
<span style="font-weight: 600;">${rateFront}(${rateBack}%)</span>
</div>`);
ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
<div style="display: flex; align-items: center; margin-right: 20px;">
<div style="width: 10px; height: 10px; border-radius: 50%; background: ${color}; margin-right: 5px;"></div>
<span>成交量</span>
</div>
<span style="font-weight: 600;">${amount}${this.subjectType === 'market' ? '万手' : '亿手'}</span>
</div>`);
// ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
// <div style="display: flex; align-items: center; margin-right: 20px;">
// <div style="width: 10px; height: 10px; border-radius: 50%; background: ${MA5Param.color}; margin-right: 5px;"></div>
// <span>${MA5Param.seriesName}</span>
// </div>
// <span style="font-weight: 600;">${MA5Param.data}</span>
// </div>`);
// ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
// <div style="display: flex; align-items: center; margin-right: 20px;">
// <div style="width: 10px; height: 10px; border-radius: 50%; background: ${MA10Param.color}; margin-right: 5px;"></div>
// <span>${MA10Param.seriesName}</span>
// </div>
// <span style="font-weight: 600;">${MA10Param.data}</span>
// </div>`);
// ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
// <div style="display: flex; align-items: center; margin-right: 20px;">
// <div style="width: 10px; height: 10px; border-radius: 50%; background: ${MA20Param.color}; margin-right: 5px;"></div>
// <span>${MA20Param.seriesName}</span>
// </div>
// <span style="font-weight: 600;">${MA20Param.data}</span>
// </div>`);
// ret.push(`<div style="display: flex; justify-content: space-between; margin-top: 8px;">
// <div style="display: flex; align-items: center; margin-right: 20px;">
// <div style="width: 10px; height: 10px; border-radius: 50%; background: ${MA30Param.color}; margin-right: 5px;"></div>
// <span>${MA30Param.seriesName}</span>
// </div>
// <span style="font-weight: 600;">${MA30Param.data}</span>
// </div>`);
return ret.join('');
},
},
axisPointer: {
link: {xAxisIndex: 'all'},
label: {
backgroundColor: '#777',
formatter: param => {
if (param.axisDimension === 'x' && param.axisIndex === 0) {
const MA5Param = param.seriesData.find(item => item.seriesName === 'MA5');
const MA10Param = param.seriesData.find(item => item.seriesName === 'MA10');
const MA20Param = param.seriesData.find(item => item.seriesName === 'MA20');
const MA30Param = param.seriesData.find(item => item.seriesName === 'MA30');
this.MA5LegendData = MA5Param.data;
this.MA10LegendData = MA10Param.data;
this.MA20LegendData = MA20Param.data;
this.MA30LegendData = MA30Param.data;
}
if (param.axisDimension === 'y') {
return round(param.value, 2);
} else {
return param.value;
}
}
}
},
visualMap: {
show: false,
seriesIndex: 5,
dimension: 2,
pieces: [{
value: 1,
color: Yred
}, {
value: -1,
color: Ygreen
}]
},
grid: [
{
left: '10%',
right: '8%',
height: '62%'
},
{
left: '10%',
right: '8%',
top: '80%',
height: '10%'
}
],
xAxis: [
{
type: 'category',
data: this.xData,
scale: true,
boundaryGap: true,
axisLine: {onZero: false},
splitLine: {show: false},
splitNumber: 20,
min: 'dataMin',
max: 'dataMax',
axisPointer: {
z: 100
},
axisTick: {
alignWithLabel: true,
},
axisLabel: {
color: fontColor,
},
},
{
type: 'category',
gridIndex: 1,
data: this.xData,
scale: true,
boundaryGap: true,
axisLine: {onZero: false},
axisTick: {show: false},
splitLine: {show: false},
axisLabel: {show: false},
splitNumber: 20,
min: 'dataMin',
max: 'dataMax'
}
],
yAxis: [
{
scale: true,
boundaryGap: ['10%', '20%'],
axisLabel: {
color: fontColor,
},
splitLine: {
lineStyle: {
type: 'dashed',
color: lineColor
}
}
},
{
scale: true,
gridIndex: 1,
splitNumber: 2,
axisLabel: {show: false},
axisLine: {show: false},
axisTick: {show: false},
splitLine: {show: false}
}
],
dataZoom,
series: [
{
name: this.title,
type: 'candlestick',
barMaxWidth: 50,
data: this.kData.map((item, index) => ({value: item, rate: this.rateData[index]})),
itemStyle: {
color: Yred,
color0: Ygreen,
borderColor: null,
borderColor0: null
},
},
//MA5
{
name: 'MA5',
type: 'line',
data: this.ma5,
smooth: true,
showSymbol: false,
lineStyle: {
opacity: 0.5,
width: 1,
color: '#4594F2',
}
},
//MA10
{
name: 'MA10',
type: 'line',
data: this.ma10,
smooth: true,
showSymbol: false,
lineStyle: {
opacity: 0.5,
width: 1,
color: '#EB81C8',
}
},
//MA20
{
name: 'MA20',
type: 'line',
data: this.ma20,
smooth: true,
showSymbol: false,
lineStyle: {
opacity: 0.5,
width: 1,
color: '#E89341',
}
},
//MA30
{
name: 'MA30',
type: 'line',
data: this.ma30,
smooth: true,
showSymbol: false,
lineStyle: {
opacity: 0.5,
width: 1,
color: '#ad66a7'
}
},
{
name: '成交量',
type: 'bar',
barMaxWidth: 50,
xAxisIndex: 1,
yAxisIndex: 1,
data: this.amountData,
}
]
};
},
},
methods: {
resize() {
myChart.resize();
},
showLoading() {
myChart.showLoading({
text: '加载中...',
color: '#000',
textColor: '#000',
//遮罩颜色
maskColor: 'rgba(255, 255, 255, 0.7)',
// 字体大小。从 `v4.8.0` 开始支持。
fontSize: 14,
});
},
setZero(value) {
let num = value.toString();
if(num.indexOf('.') === -1) {
num = num + '.00';
} else if(num.slice(num.indexOf('.') + 1).length === 1) {
num = num + '0';
}
return num;
}
},
}
</script>
<style lang="less" scoped>
@import url("../../../assets/css/variables");
.k-chart {
position: relative;
width: 100%;
height: 100%;
.legend {
position: absolute;
top: 0;
left: 0;
width: 100%;
display: flex;
justify-content: center;
padding-top: 8px;
z-index: 9999;
.title {
color: @font-color;
font-size: 13px;
font-weight: 500;
margin-right: 10px;
.titleLegend {
display: inline-block;
width: 20px;
height: 10px;
background-color: @profit-loss-red;
border-radius: 3px;
}
}
.MA {
display: inline-block;
color: @font-color;
font-size: 13px;
font-weight: 500;
color: #4594F2;
margin-right: 8px;
}
.MA5Figure {
display: inline-block;
width: 10px;
height: 10px;
background-color: #4594F2;
border-radius: 5px;
}
.MA10 {
color: #EB81C8;
.MA10Figure {
background-color: #EB81C8;
}
}
.MA20 {
color: #E89341;
.MA20Figure {
background-color: #E89341;
}
}
.MA30 {
color: #ad66a7;
.MA30Figure {
background-color: #ad66a7;
}
}
}
.chart {
width: 100%;
height: 100%;
}
}
</style>
クチャート
おすすめ
転載: blog.csdn.net/honeymoon_/article/details/130504606
おすすめ
ランキング