chart-model.js
6.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/**
* echarts demo: https://www.echartsjs.com/examples/zh/editor.html?c=line-simple
* 通过输入固定格式的 echarts dataset,得到 echarts options
*
* TODO X轴 formatter:1.只针对 X轴,不针对 tooltip 2. tooltip 和 X轴同时生效
* TODO legend(series) 到多Y轴的映射
*/
import _ from 'lodash'
export class LineChart {
constructor ({ xAxis = [], yIndexMap = {}, dataset }) {
this.chartType = 'line'
this.colors = {
series: [
{ value: '#57b2ff' },
{ value: '#d70b24' },
{ value: '#48c765' },
{ value: '#fbb64e' },
{ value: '#f95e58' }
],
labelText: {
xAxis: 'rgba(0,0,0,.6)',
yAxis: 'rgba(0,0,0,.6)'
},
splitLine: 'rgba(236, 237, 239, 0.26)'
}
this.yIndexMap = yIndexMap
this.dataset = dataset
this.keys = { xAxis: 'x', yAxis: 'y', legend: 's' }
this.legends = this.getLegends()
this.xAxis = (xAxis.length && xAxis) || this.getXAxis()
this.yAxis = this.getYAxis(this.legends)
this.series = this.getSeries(this.legends, this.xAxis)
this.tooltip = this.getTooltip()
}
/**
* 填补缺失值,说白了其实就是 merge
* seriesTemplate: [{x: '01-01', y: null}, {x: '02-01', y: null}, {x: '03-01', y: null}]
* seriesItemsFromDataSet: [{ x: '01-01', y: 11}, {x: '03-01', y: 33 }]
*
* => [{x: '01-01', y: 11}, {x: '02-01', y: null}, {x: '03-01', y: 33}]
*
* seriesTemplate (X轴所有点): ['01-01', '01-02', '01-03]
* seriesItemsFromDataSet: 可能缺失的数据作为 填充值
* options: { legend: string // 某条折线图名字 }
*
* seriesTemplate -> 循环填充默认值 -> [Object, Object]
* seriesItemsFromDataSet -> 字典{} -> 循环模板(X轴所有点)-> 找到X轴上点在 seriesItemsFromDataSet字典中的值 ->,替换模板中的默认值 -> 返回填充后的模板
*/
padMissingValues (seriesTemplate, seriesItemsFromDataSet, options) {
const { xAxis, yAxis, legend } = this.keys
const seriesItemsFromDataSetDict = seriesItemsFromDataSet.reduce((obj, curr) => (Object.assign(Object.assign({}, obj), { [curr[xAxis]]: curr })), {})
const templateWithValue = seriesTemplate.map(xAxis => {
return (seriesItemsFromDataSetDict[xAxis] || {
[legend]: options.legend,
[xAxis]: xAxis,
[yAxis]: null
})
})
return templateWithValue
}
/**
* 对于复杂的数据转换,需要写 ts,方便看懂代码之间的数据流转
* @returns: ['折线图1', '折线图2']
*/
getLegends () {
const legendKey = this.keys.legend
const allLegends = _.uniqBy(this.dataset, legendKey).map((item) => item[legendKey])
const legends = Array.from(new Set(allLegends))
return legends
}
/**
* 获取X轴的数据
* demo: ['01-01', '02-01', '03-01', '04-01']
*/
getXAxis () {
const xAxis = this.dataset.map(item => item[this.keys.xAxis])
return xAxis
}
getTooltip () {
return {
trigger: 'axis'
}
}
getDefaultYAxis (option) {
const { show, axisLabelFormatter } = option
return {
show,
type: 'value',
axisLabel: {
// Y轴文字颜色,
color: this.colors.labelText.xAxis,
// formatter: '{value}',
formatter: axisLabelFormatter
},
// 不需要对 Y 轴顶部的文字做定制
// nameTextStyle: {
// color: [yAxisTitleFontColor],
// fontSize: 14,
// fontWeight: 600,
// },
// y轴坐标轴轴线, 也就是y轴的一条竖线
axisLine: {
show: false,
lineStyle: {
// y轴上数字的颜色
fontSize: 15,
color: this.colors.labelText.yAxis
// opacity: 1
}
},
// 显示轴线与数值之间的 「-」
axisTick: { show: false },
splitLine: {
lineStyle: {
// 使用深浅的间隔色
// 类似四条三格的横线的颜色
color: [this.colors.splitLine]
}
}
}
}
/**
* Y轴数据
* @param legends
*/
getYAxis (legends) {
// TODO 如果返回值 函数怎么办呢?
function getAxisLabelFormatter (legend) {
return function (value) {
// const format = formatDict[legend] || "0.0a";
// return numeral(value).format(format);
// TODO 自己实现 formatter 函数
return value
}
}
const yAxis = legends.map(legend => this.getDefaultYAxis({
show: window.innerWidth > 768,
// TODO 外界传进来legendValueFormatter
axisLabelFormatter: getAxisLabelFormatter(legend)
}))
return yAxis
}
/**
*
* @param {*} legends
* @param {*} xAxis
*
interface Series {
name: string,
data: seriesData,
}
*/
getSeries (legends, xAxis) {
/**
interface groupbyLegend {
[legendKey1]: [
[legendKey1]: legendValue,
[xAxiskey]: xAxisValue,
[yAxiskey]: yAxisValue
]
}
*/
const groupbyLegend = _.groupBy(this.dataset, this.keys.legend)
const series = legends.map((legend, index) => {
const seriesItemsFromDataSet = groupbyLegend[legend]
const data = this.padMissingValues(xAxis, seriesItemsFromDataSet, { legend }).map(item => item[this.keys.yAxis])
return {
name: legend,
type: this.chartType,
// TODO 值和 value 对应起来
data,
yAxisIndex: this.yIndexMap[legend] || 0,
itemStyle: {
normal: {
color: this.colors.series[index].value
}
// emphasis: {
// color: '#59a4ed',
// },
}
// TODO 指定 Y轴
// TODO zlevel: 0,
}
})
return series
}
getDefaultOption () {
return {
tooltip: {
show: true,
trigger: 'axis'
},
axisTick: { show: false },
xAxis: {
type: 'category',
boundaryGap: true,
axisTick: { show: false },
axisLine: {
lineStyle: {
// x轴颜色, 包含:x轴的颜色、文字颜色
// 图例: https://jietu.qq.com/upload/index.html?image=http://jietu-10024907.file.myqcloud.com/hwfezrujmosnhjesfoweuqdguwrwyekw.jpg
color: 'rgba(0,0,0,.6)'
}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
color: this.colors.series
}
}
getOption () {
const option = {
textStyle: {
fontFamily: 'Roboto'
},
tooltip: this.tooltip,
series: this.series,
legend: {
data: this.legends,
show: true
},
xAxis: {
data: this.xAxis
},
yAxis: this.yAxis
}
return _.merge(option, this.getDefaultOption())
}
}