import { ref, watch } from "vue"; import { getTheme, ThemeEnum } from '@/common/config/theme'; import { deepMerge } from '@/utils/objHandle' import { EChartsOption } from 'echarts'; import moment from 'moment'; // 命名待优化 type Colors = { backgroundColor: string, // 图表背景颜色 axisPointerLabelColor: string, legendTextColor: string, xAxisLineColor: string yAxisLineColor: string, seriesMarkLabelColor: string, seriesMarkLineColor: string, upColor: string, downColor: string, equalColor: string, } export type Source = { date: string, // xAxis数据,必须是第一个属性 open: number, close: number, lowest: number, highest: number, ma5: string, ma10: string, ma15: string, vol: number, macd: string, dif: string, dea: string, k: string, d: string, j: string, cci: string, } // 图表数据 type ChartData = { source: Source[], } export function handleEchart() { const options = ref(); // 当前主题 const theme = getTheme(); // 图表数据 const chartData = ref({ source: [] }); // 初始化图表配置 const initOptions = (seriesType: string) => { const { source } = chartData.value; const option: EChartsOption = { dataset: { dimensions: ['date', 'open', 'close', 'lowest', 'highest', 'ma5', 'ma10', 'ma15', 'vol', 'macd', 'dif', 'dea', 'k', 'd', 'j', 'cci'], source }, animation: false, axisPointer: { link: [ { xAxisIndex: 'all' } ], }, legend: { //图例控件,点击图例控制哪些系列不显示 type: 'scroll', data: ['MA5', 'MA10', 'MA15'], selected: { VOL: seriesType === 'VOL', MACD: seriesType === 'MACD', DIF: seriesType === 'MACD', DEA: seriesType === 'MACD', K: seriesType === 'KDJ', D: seriesType === 'KDJ', J: seriesType === 'KDJ', CCI: seriesType === 'CCI', }, itemWidth: 14, itemHeight: 2, left: '5%', top: 0, textStyle: { fontSize: 12, }, }, // 悬浮框 tooltip: { trigger: 'axis', axisPointer: { type: 'cross', }, backgroundColor: 'rgba(255,255,255,.95)', borderWidth: 1, borderRadius: 3, textStyle: { color: '#4d535c', }, className: 'tooltip', formatter: (params: any) => { let result = ''; params.forEach((item: any, index: number) => { if (index === 0) { result += '
' + moment(item.data.date).format('YYYY-MM-DD HH:mm') + '
'; } if (item.seriesType === 'candlestick') { result += '
开盘' + item.data.open + '
'; result += '
收盘' + item.data.close + '
'; result += '
最低' + item.data.lowest + '
'; result += '
最高' + item.data.highest + '
'; } else { const key = item.dimensionNames[item.encode.y[0]]; result += '
' + item.seriesName + '' + item.data[key] + '
'; } }) return result; }, }, grid: [ // K线 { top: '8%', left: '8%', right: '8%', height: '55%', }, // MACD、VOL、KDJ { top: '73%', left: '8%', right: '8%', height: '20%', } ], xAxis: [ // K线时间轴 { type: 'category', axisLabel: { formatter: (val: any) => moment(val).format('YYYY/MM/DD'), }, splitLine: { show: true, }, }, // MACD、VOL、KDJ时间轴 { type: 'category', gridIndex: 1, axisLabel: { show: false, formatter: (val: any) => moment(val).format('YYYY/MM/DD'), }, axisLine: { show: false, }, axisTick: { show: false, }, splitLine: { show: true, }, } ], yAxis: [ { scale: true, }, { scale: true, gridIndex: 1, } ], dataZoom: [ { type: 'inside', xAxisIndex: [0, 1], startValue: source.length - 120, // 起始显示K线条数(最新120条) endValue: source.length, minValueSpan: 60, // 限制窗口缩放显示最少数据条数 maxValueSpan: 400, // 限制窗口缩放显示最大数据条数 }, { show: false, type: 'slider', xAxisIndex: [0, 1], }, ], series: [ { name: 'K线', type: 'candlestick', // Y轴数据 markLine: { animation: false, // 标线两端图标 symbol: 'none', // 标线标签样式 label: { fontWeight: 'bold', position: 'end', }, // 标线样式 lineStyle: { type: 'dashed', }, data: [ { // 当前价 yAxis: source.length ? source[source.length - 1].close : 0, }, ], }, }, { name: 'MA5', type: 'line', sampling: 'average', //折线图在数据量远大于像素点时候的降采样策略,开启后可以有效的优化图表的绘制效率 smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, { name: 'MA10', type: 'line', sampling: 'average', smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, { name: 'MA15', type: 'line', sampling: 'average', smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, { name: 'VOL', type: 'bar', sampling: 'average', xAxisIndex: 1, yAxisIndex: 1, barWidth: '60%', }, { name: 'MACD', type: 'bar', sampling: 'average', xAxisIndex: 1, yAxisIndex: 1, barWidth: '20%', }, { name: 'DIF', type: 'line', sampling: 'average', xAxisIndex: 1, yAxisIndex: 1, smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, { name: 'DEA', type: 'line', sampling: 'average', xAxisIndex: 1, yAxisIndex: 1, smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, { name: 'K', type: 'line', sampling: 'average', xAxisIndex: 1, yAxisIndex: 1, smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, { name: 'D', type: 'line', sampling: 'average', xAxisIndex: 1, yAxisIndex: 1, smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, { name: 'J', type: 'line', sampling: 'average', xAxisIndex: 1, yAxisIndex: 1, smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, { name: 'CCI', type: 'line', sampling: 'average', xAxisIndex: 1, yAxisIndex: 1, smooth: true, symbol: 'none', lineStyle: { width: 1, opacity: 0.8, }, }, ], }; options.value = deepMerge(option, getColors(theme.value)); }; // 动态更新数据 const updateOptions = (seriesType: string) => { const { source } = chartData.value; if (source.length) { options.value = { legend: { selected: { VOL: seriesType === 'VOL', MACD: seriesType === 'MACD', DIF: seriesType === 'MACD', DEA: seriesType === 'MACD', K: seriesType === 'KDJ', D: seriesType === 'KDJ', J: seriesType === 'KDJ', CCI: seriesType === 'CCI', }, }, dataset: { source }, series: [ { name: 'K线', markLine: { data: [ { yAxis: source[source.length - 1].close, }, ], }, }, ], } } }; // 设置图表样式 const setColors = (colors: Colors): EChartsOption => { return { // 图表背景颜色 backgroundColor: colors.backgroundColor, axisPointer: { label: { color: colors.axisPointerLabelColor, }, }, legend: { textStyle: { color: colors.legendTextColor, }, }, xAxis: [ { splitLine: { lineStyle: { // 坐标分隔线颜色 color: colors.xAxisLineColor, }, }, }, { splitLine: { lineStyle: { // 坐标分隔线颜色 color: colors.xAxisLineColor, }, }, } ], yAxis: [ { splitLine: { lineStyle: { // 坐标分隔线颜色 color: colors.xAxisLineColor, }, }, }, { splitLine: { lineStyle: { // 坐标分隔线颜色 color: colors.xAxisLineColor, }, }, } ], series: [ { name: 'K线', markLine: { // 标线标签样式 label: { color: colors.seriesMarkLabelColor, }, // 标线样式 lineStyle: { color: colors.seriesMarkLineColor, }, }, }, { name: 'MA5', }, { name: 'MA10', }, { name: 'MA15', }, { name: 'VOL', itemStyle: { color: (params: any) => { // 判断收盘价是否高于或等于开盘价 if (params.data.close >= params.data.open) { return colors.upColor; } else { return colors.downColor; } }, } }, { name: 'MACD', itemStyle: { color: (params: any) => { if (params.data.macd > 0) { return colors.upColor; } else { return colors.downColor; } }, } }, ] } } // 获取图表样式配置 const getColors = (theme: ThemeEnum) => { switch (theme) { case ThemeEnum.default: case ThemeEnum.dark: return setColors({ backgroundColor: 'transparent', axisPointerLabelColor: '#fff', legendTextColor: '#0e99e2', xAxisLineColor: '#171B1D', yAxisLineColor: '#171B1D', seriesMarkLabelColor: '#3C454B', seriesMarkLineColor: '#33393D', upColor: '#eb5454', downColor: '#47b262', equalColor: '#fff', }); case ThemeEnum.light: return setColors({ backgroundColor: 'transparent', axisPointerLabelColor: '#fff', legendTextColor: '#FC9618', xAxisLineColor: '#DAE5EC', yAxisLineColor: '#DAE5EC', seriesMarkLabelColor: '#ACB8C0', seriesMarkLineColor: '#ACB8C0', upColor: '#eb5454', downColor: '#47b262', equalColor: '#333', }); } } watch(theme, (val) => { options.value = getColors(val); }); return { chartData, options, initOptions, updateOptions, } }