Commit e4fc4173e243afacd7383984c9041661030ff0bf
1 parent
71637956
feat(component): add echarts component !#zh: 支持echarts 图表组件📈
Showing
7 changed files
with
1046 additions
and
0 deletions
front-end/h5/package.json
| @@ -21,6 +21,7 @@ | @@ -21,6 +21,7 @@ | ||
| 21 | "animate.css": "^3.7.2", | 21 | "animate.css": "^3.7.2", |
| 22 | "ant-design-vue": "^1.3.14", | 22 | "ant-design-vue": "^1.3.14", |
| 23 | "core-js": "^2.6.5", | 23 | "core-js": "^2.6.5", |
| 24 | + "echarts": "^4.8.0", | ||
| 24 | "element-ui": "^2.13.0", | 25 | "element-ui": "^2.13.0", |
| 25 | "font-awesome": "4.7.0", | 26 | "font-awesome": "4.7.0", |
| 26 | "hotkeys-js": "^3.7.6", | 27 | "hotkeys-js": "^3.7.6", |
| @@ -28,7 +29,9 @@ | @@ -28,7 +29,9 @@ | ||
| 28 | "proxy-agent": "^3.1.0", | 29 | "proxy-agent": "^3.1.0", |
| 29 | "qrcode": "^1.4.1", | 30 | "qrcode": "^1.4.1", |
| 30 | "register-service-worker": "^1.6.2", | 31 | "register-service-worker": "^1.6.2", |
| 32 | + "resize-detector": "^0.2.2", | ||
| 31 | "strapi-sdk-javascript": "^0.3.1", | 33 | "strapi-sdk-javascript": "^0.3.1", |
| 34 | + "v-charts": "^1.19.0", | ||
| 32 | "v-click-outside": "^2.1.4", | 35 | "v-click-outside": "^2.1.4", |
| 33 | "vant": "^2.2.12", | 36 | "vant": "^2.2.12", |
| 34 | "vue": "^2.6.10", | 37 | "vue": "^2.6.10", |
front-end/h5/src/components/plugins/charts/chart-mixin.js
0 → 100644
| 1 | +import echarts from 'echarts/lib/echarts' | ||
| 2 | +import debounce from 'lodash/debounce' | ||
| 3 | +import { addListener, removeListener } from 'resize-detector' | ||
| 4 | +import { LineChart } from './chart-model' | ||
| 5 | + | ||
| 6 | +// TODO 工具函数:获取某个时间段内的时间 | ||
| 7 | +function addDays (date, dayStep = 1) { | ||
| 8 | + const next = new Date(date) | ||
| 9 | + next.setDate(next.getDate() + dayStep) | ||
| 10 | + return next | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +function dateRange (start, end, range = []) { | ||
| 14 | + start = new Date(start) | ||
| 15 | + end = new Date(end) | ||
| 16 | + if (start > end) return range | ||
| 17 | + range = [start] | ||
| 18 | + while (start < end) { | ||
| 19 | + start = addDays(start, 1) | ||
| 20 | + range.push(start) | ||
| 21 | + } | ||
| 22 | + // const next = addDays(start, 1) | ||
| 23 | + // return dateRange(next, end, [...range, start]) | ||
| 24 | + return range | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +export default { | ||
| 28 | + watch: { | ||
| 29 | + dataset: { | ||
| 30 | + handler (items) { | ||
| 31 | + this.renderChart() | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + }, | ||
| 35 | + methods: { | ||
| 36 | + getXAxis () { | ||
| 37 | + const [start, end] = this.dashboard.currentFilterState.daterange | ||
| 38 | + if (start === end) return Array.from({ length: 24 }, (_, i) => `${i}`.padStart(2, '0')) | ||
| 39 | + return dateRange(start, end).map(date => date.toISOString().slice(0, 10)) | ||
| 40 | + }, | ||
| 41 | + /** | ||
| 42 | + * | ||
| 43 | + */ | ||
| 44 | + getDataSet () { | ||
| 45 | + // const [start, end] = daterange | ||
| 46 | + // if (start === end) { | ||
| 47 | + // // return this.dataset.map(item => ({...item, s: s.replace(/[\d]{4}-[\d]{2}-[\d]{2}\s+([\d]{2}):[\d]{2}:[\d]{2}/, '$1')) | ||
| 48 | + // return this.dataset.map(item => ({ | ||
| 49 | + // ...item, | ||
| 50 | + // x: `${new Date(item.x).getHours()}`.padStart(2, '0') | ||
| 51 | + // })) | ||
| 52 | + // } | ||
| 53 | + return this.dataset | ||
| 54 | + }, | ||
| 55 | + renderChart () { | ||
| 56 | + const option = new LineChart({ | ||
| 57 | + dataset: this.getDataSet(), | ||
| 58 | + yIndexMap: { count: 0, sum_base_cpm: 1 } | ||
| 59 | + // xAxis: this.getXAxis() | ||
| 60 | + }).getOption() | ||
| 61 | + // this.option = Object.freeze(option) | ||
| 62 | + this.$nextTick(() => { | ||
| 63 | + const ele = this.$refs.chart | ||
| 64 | + if (ele) { | ||
| 65 | + // const chart = window.echarts.init(ele) | ||
| 66 | + const chart = echarts.init(ele) | ||
| 67 | + this.chart = chart | ||
| 68 | + chart.clear() | ||
| 69 | + chart.setOption(option) | ||
| 70 | + } | ||
| 71 | + }) | ||
| 72 | + }, | ||
| 73 | + autoResize () { | ||
| 74 | + this.lastArea = this.getArea() | ||
| 75 | + this.__resizeHandler = debounce( | ||
| 76 | + () => { | ||
| 77 | + if (this.lastArea === 0) { | ||
| 78 | + // emulate initial render for initially hidden charts | ||
| 79 | + this.mergeOptions({}, true) | ||
| 80 | + this.resize() | ||
| 81 | + this.mergeOptions(this.options || this.manualOptions || {}, true) | ||
| 82 | + } else { | ||
| 83 | + this.resize() | ||
| 84 | + } | ||
| 85 | + this.lastArea = this.getArea() | ||
| 86 | + }, | ||
| 87 | + 100, | ||
| 88 | + { leading: true } | ||
| 89 | + ) | ||
| 90 | + addListener(this.$el, this.__resizeHandler) | ||
| 91 | + } | ||
| 92 | + }, | ||
| 93 | + mounted () { | ||
| 94 | + this.__resizeHandler = debounce( | ||
| 95 | + () => { | ||
| 96 | + this.chart.resize() | ||
| 97 | + }, | ||
| 98 | + 500, | ||
| 99 | + { leading: true } | ||
| 100 | + ) | ||
| 101 | + addListener(this.$el, this.__resizeHandler) | ||
| 102 | + }, | ||
| 103 | + destroy () { | ||
| 104 | + removeListener(this.$el, this.__resizeHandler) | ||
| 105 | + } | ||
| 106 | +} |
front-end/h5/src/components/plugins/charts/chart-model.js
0 → 100644
| 1 | +/** | ||
| 2 | + * echarts demo: https://www.echartsjs.com/examples/zh/editor.html?c=line-simple | ||
| 3 | + * 通过输入固定格式的 echarts dataset,得到 echarts options | ||
| 4 | + * | ||
| 5 | + * TODO X轴 formatter:1.只针对 X轴,不针对 tooltip 2. tooltip 和 X轴同时生效 | ||
| 6 | + * TODO legend(series) 到多Y轴的映射 | ||
| 7 | + */ | ||
| 8 | +import _ from 'lodash' | ||
| 9 | + | ||
| 10 | +export class LineChart { | ||
| 11 | + constructor ({ xAxis = [], yIndexMap = {}, dataset }) { | ||
| 12 | + this.chartType = 'line' | ||
| 13 | + this.colors = { | ||
| 14 | + series: [ | ||
| 15 | + { value: '#57b2ff' }, | ||
| 16 | + { value: '#d70b24' }, | ||
| 17 | + { value: '#48c765' }, | ||
| 18 | + { value: '#fbb64e' }, | ||
| 19 | + { value: '#f95e58' } | ||
| 20 | + ], | ||
| 21 | + labelText: { | ||
| 22 | + xAxis: 'rgba(0,0,0,.6)', | ||
| 23 | + yAxis: 'rgba(0,0,0,.6)' | ||
| 24 | + }, | ||
| 25 | + splitLine: 'rgba(236, 237, 239, 0.26)' | ||
| 26 | + } | ||
| 27 | + this.yIndexMap = yIndexMap | ||
| 28 | + this.dataset = dataset | ||
| 29 | + this.keys = { xAxis: 'x', yAxis: 'y', legend: 's' } | ||
| 30 | + this.legends = this.getLegends() | ||
| 31 | + this.xAxis = (xAxis.length && xAxis) || this.getXAxis() | ||
| 32 | + this.yAxis = this.getYAxis(this.legends) | ||
| 33 | + this.series = this.getSeries(this.legends, this.xAxis) | ||
| 34 | + this.tooltip = this.getTooltip() | ||
| 35 | + } | ||
| 36 | + /** | ||
| 37 | + * 填补缺失值,说白了其实就是 merge | ||
| 38 | + * seriesTemplate: [{x: '01-01', y: null}, {x: '02-01', y: null}, {x: '03-01', y: null}] | ||
| 39 | + * seriesItemsFromDataSet: [{ x: '01-01', y: 11}, {x: '03-01', y: 33 }] | ||
| 40 | + * | ||
| 41 | + * => [{x: '01-01', y: 11}, {x: '02-01', y: null}, {x: '03-01', y: 33}] | ||
| 42 | + * | ||
| 43 | + * seriesTemplate (X轴所有点): ['01-01', '01-02', '01-03] | ||
| 44 | + * seriesItemsFromDataSet: 可能缺失的数据作为 填充值 | ||
| 45 | + * options: { legend: string // 某条折线图名字 } | ||
| 46 | + * | ||
| 47 | + * seriesTemplate -> 循环填充默认值 -> [Object, Object] | ||
| 48 | + * seriesItemsFromDataSet -> 字典{} -> 循环模板(X轴所有点)-> 找到X轴上点在 seriesItemsFromDataSet字典中的值 ->,替换模板中的默认值 -> 返回填充后的模板 | ||
| 49 | + */ | ||
| 50 | + padMissingValues (seriesTemplate, seriesItemsFromDataSet, options) { | ||
| 51 | + const { xAxis, yAxis, legend } = this.keys | ||
| 52 | + const seriesItemsFromDataSetDict = seriesItemsFromDataSet.reduce((obj, curr) => (Object.assign(Object.assign({}, obj), { [curr[xAxis]]: curr })), {}) | ||
| 53 | + const templateWithValue = seriesTemplate.map(xAxis => { | ||
| 54 | + return (seriesItemsFromDataSetDict[xAxis] || { | ||
| 55 | + [legend]: options.legend, | ||
| 56 | + [xAxis]: xAxis, | ||
| 57 | + [yAxis]: null | ||
| 58 | + }) | ||
| 59 | + }) | ||
| 60 | + return templateWithValue | ||
| 61 | + } | ||
| 62 | + /** | ||
| 63 | + * 对于复杂的数据转换,需要写 ts,方便看懂代码之间的数据流转 | ||
| 64 | + * @returns: ['折线图1', '折线图2'] | ||
| 65 | + */ | ||
| 66 | + getLegends () { | ||
| 67 | + const legendKey = this.keys.legend | ||
| 68 | + const allLegends = _.uniqBy(this.dataset, legendKey).map((item) => item[legendKey]) | ||
| 69 | + const legends = Array.from(new Set(allLegends)) | ||
| 70 | + return legends | ||
| 71 | + } | ||
| 72 | + /** | ||
| 73 | + * 获取X轴的数据 | ||
| 74 | + * demo: ['01-01', '02-01', '03-01', '04-01'] | ||
| 75 | + */ | ||
| 76 | + getXAxis () { | ||
| 77 | + const xAxis = this.dataset.map(item => item[this.keys.xAxis]) | ||
| 78 | + return xAxis | ||
| 79 | + } | ||
| 80 | + getTooltip () { | ||
| 81 | + return { | ||
| 82 | + trigger: 'axis' | ||
| 83 | + } | ||
| 84 | + } | ||
| 85 | + getDefaultYAxis (option) { | ||
| 86 | + const { show, axisLabelFormatter } = option | ||
| 87 | + return { | ||
| 88 | + show, | ||
| 89 | + type: 'value', | ||
| 90 | + axisLabel: { | ||
| 91 | + // Y轴文字颜色, | ||
| 92 | + color: this.colors.labelText.xAxis, | ||
| 93 | + // formatter: '{value}', | ||
| 94 | + formatter: axisLabelFormatter | ||
| 95 | + }, | ||
| 96 | + // 不需要对 Y 轴顶部的文字做定制 | ||
| 97 | + // nameTextStyle: { | ||
| 98 | + // color: [yAxisTitleFontColor], | ||
| 99 | + // fontSize: 14, | ||
| 100 | + // fontWeight: 600, | ||
| 101 | + // }, | ||
| 102 | + // y轴坐标轴轴线, 也就是y轴的一条竖线 | ||
| 103 | + axisLine: { | ||
| 104 | + show: false, | ||
| 105 | + lineStyle: { | ||
| 106 | + // y轴上数字的颜色 | ||
| 107 | + fontSize: 15, | ||
| 108 | + color: this.colors.labelText.yAxis | ||
| 109 | + // opacity: 1 | ||
| 110 | + } | ||
| 111 | + }, | ||
| 112 | + // 显示轴线与数值之间的 「-」 | ||
| 113 | + axisTick: { show: false }, | ||
| 114 | + splitLine: { | ||
| 115 | + lineStyle: { | ||
| 116 | + // 使用深浅的间隔色 | ||
| 117 | + // 类似四条三格的横线的颜色 | ||
| 118 | + color: [this.colors.splitLine] | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + } | ||
| 123 | + /** | ||
| 124 | + * Y轴数据 | ||
| 125 | + * @param legends | ||
| 126 | + */ | ||
| 127 | + getYAxis (legends) { | ||
| 128 | + // TODO 如果返回值 函数怎么办呢? | ||
| 129 | + function getAxisLabelFormatter (legend) { | ||
| 130 | + return function (value) { | ||
| 131 | + // const format = formatDict[legend] || "0.0a"; | ||
| 132 | + // return numeral(value).format(format); | ||
| 133 | + // TODO 自己实现 formatter 函数 | ||
| 134 | + return value | ||
| 135 | + } | ||
| 136 | + } | ||
| 137 | + const yAxis = legends.map(legend => this.getDefaultYAxis({ | ||
| 138 | + show: window.innerWidth > 768, | ||
| 139 | + // TODO 外界传进来legendValueFormatter | ||
| 140 | + axisLabelFormatter: getAxisLabelFormatter(legend) | ||
| 141 | + })) | ||
| 142 | + return yAxis | ||
| 143 | + } | ||
| 144 | + /** | ||
| 145 | + * | ||
| 146 | + * @param {*} legends | ||
| 147 | + * @param {*} xAxis | ||
| 148 | + * | ||
| 149 | + interface Series { | ||
| 150 | + name: string, | ||
| 151 | + data: seriesData, | ||
| 152 | + | ||
| 153 | + } | ||
| 154 | + */ | ||
| 155 | + getSeries (legends, xAxis) { | ||
| 156 | + /** | ||
| 157 | + interface groupbyLegend { | ||
| 158 | + [legendKey1]: [ | ||
| 159 | + [legendKey1]: legendValue, | ||
| 160 | + [xAxiskey]: xAxisValue, | ||
| 161 | + [yAxiskey]: yAxisValue | ||
| 162 | + ] | ||
| 163 | + } | ||
| 164 | + */ | ||
| 165 | + const groupbyLegend = _.groupBy(this.dataset, this.keys.legend) | ||
| 166 | + const series = legends.map((legend, index) => { | ||
| 167 | + const seriesItemsFromDataSet = groupbyLegend[legend] | ||
| 168 | + const data = this.padMissingValues(xAxis, seriesItemsFromDataSet, { legend }).map(item => item[this.keys.yAxis]) | ||
| 169 | + return { | ||
| 170 | + name: legend, | ||
| 171 | + type: this.chartType, | ||
| 172 | + // TODO 值和 value 对应起来 | ||
| 173 | + data, | ||
| 174 | + yAxisIndex: this.yIndexMap[legend] || 0, | ||
| 175 | + itemStyle: { | ||
| 176 | + normal: { | ||
| 177 | + color: this.colors.series[index].value | ||
| 178 | + } | ||
| 179 | + // emphasis: { | ||
| 180 | + // color: '#59a4ed', | ||
| 181 | + // }, | ||
| 182 | + } | ||
| 183 | + // TODO 指定 Y轴 | ||
| 184 | + // TODO zlevel: 0, | ||
| 185 | + } | ||
| 186 | + }) | ||
| 187 | + return series | ||
| 188 | + } | ||
| 189 | + getDefaultOption () { | ||
| 190 | + return { | ||
| 191 | + tooltip: { | ||
| 192 | + show: true, | ||
| 193 | + trigger: 'axis' | ||
| 194 | + }, | ||
| 195 | + axisTick: { show: false }, | ||
| 196 | + xAxis: { | ||
| 197 | + type: 'category', | ||
| 198 | + boundaryGap: true, | ||
| 199 | + axisTick: { show: false }, | ||
| 200 | + axisLine: { | ||
| 201 | + lineStyle: { | ||
| 202 | + // x轴颜色, 包含:x轴的颜色、文字颜色 | ||
| 203 | + // 图例: https://jietu.qq.com/upload/index.html?image=http://jietu-10024907.file.myqcloud.com/hwfezrujmosnhjesfoweuqdguwrwyekw.jpg | ||
| 204 | + color: 'rgba(0,0,0,.6)' | ||
| 205 | + } | ||
| 206 | + } | ||
| 207 | + }, | ||
| 208 | + grid: { | ||
| 209 | + left: '3%', | ||
| 210 | + right: '4%', | ||
| 211 | + bottom: '3%', | ||
| 212 | + containLabel: true | ||
| 213 | + }, | ||
| 214 | + color: this.colors.series | ||
| 215 | + } | ||
| 216 | + } | ||
| 217 | + getOption () { | ||
| 218 | + const option = { | ||
| 219 | + textStyle: { | ||
| 220 | + fontFamily: 'Roboto' | ||
| 221 | + }, | ||
| 222 | + tooltip: this.tooltip, | ||
| 223 | + series: this.series, | ||
| 224 | + legend: { | ||
| 225 | + data: this.legends, | ||
| 226 | + show: true | ||
| 227 | + }, | ||
| 228 | + xAxis: { | ||
| 229 | + data: this.xAxis | ||
| 230 | + }, | ||
| 231 | + yAxis: this.yAxis | ||
| 232 | + } | ||
| 233 | + return _.merge(option, this.getDefaultOption()) | ||
| 234 | + } | ||
| 235 | +} |
front-end/h5/src/components/plugins/charts/chart-model.ts
0 → 100644
| 1 | +/** | ||
| 2 | + * echarts demo: https://www.echartsjs.com/examples/zh/editor.html?c=line-simple | ||
| 3 | + * 通过输入固定格式的 echarts dataset,得到 echarts options | ||
| 4 | + * | ||
| 5 | + * TODO X轴 formatter:1.只针对 X轴,不针对 tooltip 2. tooltip 和 X轴同时生效 | ||
| 6 | + * TODO legend(series) 到多Y轴的映射 | ||
| 7 | + */ | ||
| 8 | +import _ from "lodash"; | ||
| 9 | +// import numeral from "numeral"; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * | ||
| 13 | + yIndexMap: { | ||
| 14 | + 系列1: 0, | ||
| 15 | + 系列2: 1 | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + const users = [ | ||
| 19 | + { | ||
| 20 | + x: "2010/01/01 00:00:00", | ||
| 21 | + y: 500, | ||
| 22 | + s: "系列1" | ||
| 23 | + }, | ||
| 24 | + { | ||
| 25 | + x: "2010/01/01 00:00:00", | ||
| 26 | + y: 180, | ||
| 27 | + s: "系列2" | ||
| 28 | + }, | ||
| 29 | + { | ||
| 30 | + x: "2010/02/01 00:00:00", | ||
| 31 | + y: 250, | ||
| 32 | + s: "系列1" | ||
| 33 | + }, | ||
| 34 | + { | ||
| 35 | + x: "2010/02/01 00:00:00", | ||
| 36 | + y: 100, | ||
| 37 | + s: "系列2" | ||
| 38 | + }, | ||
| 39 | + { | ||
| 40 | + x: "2010/03/01 00:00:00", | ||
| 41 | + y: 325, | ||
| 42 | + s: "系列1" | ||
| 43 | + }, | ||
| 44 | + { | ||
| 45 | + x: "2010/03/01 00:00:00", | ||
| 46 | + y: 175, | ||
| 47 | + s: "系列2" | ||
| 48 | + }, | ||
| 49 | + { | ||
| 50 | + x: "2010/04/01 00:00:00", | ||
| 51 | + y: 190, | ||
| 52 | + s: "系列1" | ||
| 53 | + }, | ||
| 54 | + { | ||
| 55 | + x: "2010/04/01 00:00:00", | ||
| 56 | + y: 110, | ||
| 57 | + s: "系列2" | ||
| 58 | + }, | ||
| 59 | + { | ||
| 60 | + x: "2010/05/01 00:00:00", | ||
| 61 | + y: 260, | ||
| 62 | + s: "系列1" | ||
| 63 | + }, | ||
| 64 | + { | ||
| 65 | + x: "2010/05/01 00:00:00", | ||
| 66 | + y: 60, | ||
| 67 | + s: "系列2" | ||
| 68 | + }, | ||
| 69 | + { | ||
| 70 | + x: "2010/05/19 00:00:00", | ||
| 71 | + y: 90, | ||
| 72 | + s: "系列2" | ||
| 73 | + }, | ||
| 74 | + { | ||
| 75 | + x: "2010/05/23 00:00:00", | ||
| 76 | + y: 90, | ||
| 77 | + s: "系列2" | ||
| 78 | + } | ||
| 79 | + ]; | ||
| 80 | + | ||
| 81 | + * | ||
| 82 | + */ | ||
| 83 | + | ||
| 84 | +// 固定格式 | ||
| 85 | +interface DataSetItem { | ||
| 86 | + x: string; // x轴 | ||
| 87 | + y: string | number; // y轴,也就是 value | ||
| 88 | + s: string | number; // s 也就是 legend | ||
| 89 | + [propName: string]: any; | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +interface SeriesItemDict { | ||
| 93 | + [propName: string]: any; | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +interface SeriesItem { | ||
| 97 | + name?: string; // 可选 | ||
| 98 | + type: string; | ||
| 99 | + data: Array<string | number>; | ||
| 100 | + itemStyle: { | ||
| 101 | + normal: { | ||
| 102 | + color: string; | ||
| 103 | + }; | ||
| 104 | + // emphasis: { | ||
| 105 | + // color: '#59a4ed', | ||
| 106 | + // }, | ||
| 107 | + }; | ||
| 108 | + yAxisIndex?: number | ||
| 109 | + // TODO 指定 Y轴 | ||
| 110 | + // TODO zlevel: 0, | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +interface YAxisOption { | ||
| 114 | + show: boolean; | ||
| 115 | + axisLabelFormatter: (value: any) => any; | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +interface formatDictType { | ||
| 119 | + [propName: string]: string; | ||
| 120 | +} | ||
| 121 | + | ||
| 122 | +interface uniqByResultType { | ||
| 123 | + [legendKey: string]: string // legendValue | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +interface LineChartConfig { | ||
| 127 | + xAxis?: Array<string|number>; | ||
| 128 | + dataset: DataSetItem[]; | ||
| 129 | + yIndexMap: { | ||
| 130 | + [legendkey: string]: number | ||
| 131 | + } | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +const formatDict: formatDictType = { | ||
| 135 | + gp: "$0.00", | ||
| 136 | + gross_profit: "$0.00", | ||
| 137 | + revenue: "$0.00", | ||
| 138 | + reveue_f: "$0.00", | ||
| 139 | + cvr: "0.00%", | ||
| 140 | + click_count: "0,00", | ||
| 141 | + conv: "0,00", | ||
| 142 | + show_rate: "0.00%", | ||
| 143 | + fill_rate: "0.00%" | ||
| 144 | +}; | ||
| 145 | + | ||
| 146 | +export class LineChart { | ||
| 147 | + // 指定折线采用哪一条Y轴 | ||
| 148 | + yIndexMap: { | ||
| 149 | + // 纬度/折线 | ||
| 150 | + [legendName: string]: number | ||
| 151 | + }; | ||
| 152 | + chartType: string; // 'line' | 'bar' | 'pie' | ||
| 153 | + // TODO class 里面如何定义 ts | ||
| 154 | + colors: { | ||
| 155 | + series: any; | ||
| 156 | + labelText: any; | ||
| 157 | + splitLine: string; | ||
| 158 | + }; | ||
| 159 | + // 数据源 | ||
| 160 | + dataset: DataSetItem[]; | ||
| 161 | + keys: { | ||
| 162 | + xAxis: string; | ||
| 163 | + yAxis: string; | ||
| 164 | + legend: string; | ||
| 165 | + }; | ||
| 166 | + legends: string[]; | ||
| 167 | + xAxis: Array<string | number>; | ||
| 168 | + yAxis: any[]; | ||
| 169 | + series: Array<SeriesItem>; | ||
| 170 | + tooltip: any; | ||
| 171 | + | ||
| 172 | + constructor({ | ||
| 173 | + xAxis = [], | ||
| 174 | + yIndexMap = {}, | ||
| 175 | + dataset | ||
| 176 | + } : LineChartConfig | ||
| 177 | + ) { | ||
| 178 | + this.chartType = "line"; | ||
| 179 | + this.colors = { | ||
| 180 | + series: [ | ||
| 181 | + { value: "#57b2ff" }, | ||
| 182 | + { value: "#d70b24" }, | ||
| 183 | + { value: "#48c765" }, | ||
| 184 | + { value: "#fbb64e" }, | ||
| 185 | + { value: "#f95e58" } | ||
| 186 | + ], | ||
| 187 | + labelText: { | ||
| 188 | + xAxis: "rgba(0,0,0,.6)", | ||
| 189 | + yAxis: "rgba(0,0,0,.6)" | ||
| 190 | + }, | ||
| 191 | + splitLine: "rgba(236, 237, 239, 0.26)" | ||
| 192 | + }; | ||
| 193 | + this.yIndexMap = yIndexMap; | ||
| 194 | + this.dataset = dataset; | ||
| 195 | + this.keys = { xAxis: "x", yAxis: "y", legend: "s" }; | ||
| 196 | + | ||
| 197 | + this.legends = this.getLegends(); | ||
| 198 | + this.xAxis = (xAxis.length && xAxis) || this.getXAxis(); | ||
| 199 | + this.yAxis = this.getYAxis(this.legends); | ||
| 200 | + this.series = this.getSeries(this.legends, this.xAxis); | ||
| 201 | + this.tooltip = this.getTooltip(); | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + /** | ||
| 205 | + * 填补缺失值,说白了其实就是 merge | ||
| 206 | + * seriesTemplate: [{x: '01-01', y: null}, {x: '02-01', y: null}, {x: '03-01', y: null}] | ||
| 207 | + * seriesItemsFromDataSet: [{ x: '01-01', y: 11}, {x: '03-01', y: 33 }] | ||
| 208 | + * | ||
| 209 | + * => [{x: '01-01', y: 11}, {x: '02-01', y: null}, {x: '03-01', y: 33}] | ||
| 210 | + * | ||
| 211 | + * seriesTemplate (X轴所有点): ['01-01', '01-02', '01-03] | ||
| 212 | + * seriesItemsFromDataSet: 可能缺失的数据作为 填充值 | ||
| 213 | + * options: { legend: string // 某条折线图名字 } | ||
| 214 | + * | ||
| 215 | + * seriesTemplate -> 循环填充默认值 -> [Object, Object] | ||
| 216 | + * seriesItemsFromDataSet -> 字典{} -> 循环模板(X轴所有点)-> 找到X轴上点在 seriesItemsFromDataSet字典中的值 ->,替换模板中的默认值 -> 返回填充后的模板 | ||
| 217 | + */ | ||
| 218 | + padMissingValues( | ||
| 219 | + seriesTemplate: Array<string | number>, | ||
| 220 | + seriesItemsFromDataSet: DataSetItem[], | ||
| 221 | + options: { legend: string } | ||
| 222 | + ): DataSetItem[] { | ||
| 223 | + const { xAxis, yAxis, legend } = this.keys; | ||
| 224 | + | ||
| 225 | + const seriesItemsFromDataSetDict: SeriesItemDict = seriesItemsFromDataSet.reduce( | ||
| 226 | + (obj, curr: DataSetItem) => ({ | ||
| 227 | + ...obj, | ||
| 228 | + [curr[xAxis]]: curr | ||
| 229 | + }), | ||
| 230 | + {} | ||
| 231 | + ); | ||
| 232 | + | ||
| 233 | + const templateWithValue = seriesTemplate.map(xAxis => { | ||
| 234 | + return ( | ||
| 235 | + seriesItemsFromDataSetDict[xAxis] || { | ||
| 236 | + [legend]: options.legend, | ||
| 237 | + [xAxis]: xAxis, | ||
| 238 | + [yAxis]: null | ||
| 239 | + } | ||
| 240 | + ); | ||
| 241 | + }); | ||
| 242 | + | ||
| 243 | + return templateWithValue; | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + /** | ||
| 247 | + * 对于复杂的数据转换,需要写 ts,方便看懂代码之间的数据流转 | ||
| 248 | + * @returns: ['折线图1', '折线图2'] | ||
| 249 | + */ | ||
| 250 | + getLegends(): string[] { | ||
| 251 | + const legendKey = this.keys.legend; | ||
| 252 | + const allLegends: string[] = _.uniqBy(this.dataset, legendKey).map( | ||
| 253 | + (item: uniqByResultType) => item[legendKey] | ||
| 254 | + ); | ||
| 255 | + const legends: string[] = Array.from(new Set(allLegends)); | ||
| 256 | + return legends; | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + /** | ||
| 260 | + * 获取X轴的数据 | ||
| 261 | + * demo: ['01-01', '02-01', '03-01', '04-01'] | ||
| 262 | + */ | ||
| 263 | + getXAxis(): Array<string | number> { | ||
| 264 | + const xAxis = this.dataset.map(item => item[this.keys.xAxis]); | ||
| 265 | + return xAxis; | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + getTooltip(): any { | ||
| 269 | + return { | ||
| 270 | + trigger: "axis" | ||
| 271 | + }; | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + getDefaultYAxis(option: YAxisOption) { | ||
| 275 | + const { show, axisLabelFormatter } = option; | ||
| 276 | + return { | ||
| 277 | + show, | ||
| 278 | + type: "value", | ||
| 279 | + axisLabel: { | ||
| 280 | + // Y轴文字颜色, | ||
| 281 | + color: this.colors.labelText.xAxis, | ||
| 282 | + // formatter: '{value}', | ||
| 283 | + formatter: axisLabelFormatter | ||
| 284 | + }, | ||
| 285 | + // 不需要对 Y 轴顶部的文字做定制 | ||
| 286 | + // nameTextStyle: { | ||
| 287 | + // color: [yAxisTitleFontColor], | ||
| 288 | + // fontSize: 14, | ||
| 289 | + // fontWeight: 600, | ||
| 290 | + // }, | ||
| 291 | + // y轴坐标轴轴线, 也就是y轴的一条竖线 | ||
| 292 | + axisLine: { | ||
| 293 | + show: false, | ||
| 294 | + lineStyle: { | ||
| 295 | + // y轴上数字的颜色 | ||
| 296 | + fontSize: 15, | ||
| 297 | + color: this.colors.labelText.yAxis | ||
| 298 | + // opacity: 1 | ||
| 299 | + } | ||
| 300 | + }, | ||
| 301 | + // 显示轴线与数值之间的 「-」 | ||
| 302 | + axisTick: { show: false }, | ||
| 303 | + splitLine: { | ||
| 304 | + lineStyle: { | ||
| 305 | + // 使用深浅的间隔色 | ||
| 306 | + // 类似四条三格的横线的颜色 | ||
| 307 | + color: [this.colors.splitLine] | ||
| 308 | + } | ||
| 309 | + } | ||
| 310 | + }; | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + /** | ||
| 314 | + * Y轴数据 | ||
| 315 | + * @param legends | ||
| 316 | + */ | ||
| 317 | + getYAxis(legends: string[]): any[] { | ||
| 318 | + // TODO 如果返回值 函数怎么办呢? | ||
| 319 | + function getAxisLabelFormatter(legend: string) { | ||
| 320 | + return function(value: any) { | ||
| 321 | + // const format = formatDict[legend] || "0.0a"; | ||
| 322 | + // return numeral(value).format(format); | ||
| 323 | + // TODO 自己实现 formatter 函数 | ||
| 324 | + return value | ||
| 325 | + }; | ||
| 326 | + } | ||
| 327 | + const yAxis = legends.map(legend => | ||
| 328 | + this.getDefaultYAxis({ | ||
| 329 | + show: window.innerWidth > 768, | ||
| 330 | + // TODO 外界传进来legendValueFormatter | ||
| 331 | + axisLabelFormatter: getAxisLabelFormatter(legend) | ||
| 332 | + }) | ||
| 333 | + ); | ||
| 334 | + return yAxis; | ||
| 335 | + } | ||
| 336 | + | ||
| 337 | + /** | ||
| 338 | + * | ||
| 339 | + * @param {*} legends | ||
| 340 | + * @param {*} xAxis | ||
| 341 | + * | ||
| 342 | + interface Series { | ||
| 343 | + name: string, | ||
| 344 | + data: seriesData, | ||
| 345 | + | ||
| 346 | + } | ||
| 347 | + */ | ||
| 348 | + getSeries(legends: string[], xAxis: Array<string | number>): SeriesItem[] { | ||
| 349 | + /** | ||
| 350 | + interface groupbyLegend { | ||
| 351 | + [legendKey1]: [ | ||
| 352 | + [legendKey1]: legendValue, | ||
| 353 | + [xAxiskey]: xAxisValue, | ||
| 354 | + [yAxiskey]: yAxisValue | ||
| 355 | + ] | ||
| 356 | + } | ||
| 357 | + */ | ||
| 358 | + const groupbyLegend = _.groupBy(this.dataset, this.keys.legend); | ||
| 359 | + const series = legends.map((legend: string, index: number) => { | ||
| 360 | + const seriesItemsFromDataSet: DataSetItem[] = groupbyLegend[legend]; | ||
| 361 | + const data: Array<string | number> = this.padMissingValues( | ||
| 362 | + xAxis, | ||
| 363 | + seriesItemsFromDataSet, | ||
| 364 | + { legend } | ||
| 365 | + ).map(item => item[this.keys.yAxis]); | ||
| 366 | + return { | ||
| 367 | + name: legend, // 可选 | ||
| 368 | + type: this.chartType, | ||
| 369 | + // TODO 值和 value 对应起来 | ||
| 370 | + data, | ||
| 371 | + yAxisIndex: this.yIndexMap[legend] || 0, | ||
| 372 | + itemStyle: { | ||
| 373 | + normal: { | ||
| 374 | + color: this.colors.series[index].value | ||
| 375 | + } | ||
| 376 | + // emphasis: { | ||
| 377 | + // color: '#59a4ed', | ||
| 378 | + // }, | ||
| 379 | + } | ||
| 380 | + // TODO 指定 Y轴 | ||
| 381 | + // TODO zlevel: 0, | ||
| 382 | + }; | ||
| 383 | + }); | ||
| 384 | + return series; | ||
| 385 | + } | ||
| 386 | + | ||
| 387 | + getDefaultOption() { | ||
| 388 | + return { | ||
| 389 | + tooltip: { | ||
| 390 | + show: true, | ||
| 391 | + trigger: "axis" | ||
| 392 | + }, | ||
| 393 | + axisTick: { show: false }, | ||
| 394 | + xAxis: { | ||
| 395 | + type: "category", | ||
| 396 | + boundaryGap: true, | ||
| 397 | + axisTick: { show: false }, | ||
| 398 | + axisLine: { | ||
| 399 | + lineStyle: { | ||
| 400 | + // x轴颜色, 包含:x轴的颜色、文字颜色 | ||
| 401 | + // 图例: https://jietu.qq.com/upload/index.html?image=http://jietu-10024907.file.myqcloud.com/hwfezrujmosnhjesfoweuqdguwrwyekw.jpg | ||
| 402 | + color: "rgba(0,0,0,.6)" | ||
| 403 | + } | ||
| 404 | + } | ||
| 405 | + }, | ||
| 406 | + grid: { | ||
| 407 | + y: 40, // 图表和legend之间的距离 | ||
| 408 | + left: "5%", // 图表的x轴的零点距离容器左侧的距离 | ||
| 409 | + right: "4%", // 图表的x轴的最右点距离容器右侧的距离 | ||
| 410 | + bottom: 30 | ||
| 411 | + }, | ||
| 412 | + color: this.colors.series | ||
| 413 | + }; | ||
| 414 | + } | ||
| 415 | + | ||
| 416 | + getOption() { | ||
| 417 | + const option = { | ||
| 418 | + textStyle: { | ||
| 419 | + fontFamily: "Roboto" | ||
| 420 | + }, | ||
| 421 | + tooltip: this.tooltip, | ||
| 422 | + series: this.series, | ||
| 423 | + legend: { | ||
| 424 | + data: this.legends, | ||
| 425 | + show: true | ||
| 426 | + }, | ||
| 427 | + xAxis: { | ||
| 428 | + data: this.xAxis | ||
| 429 | + }, | ||
| 430 | + yAxis: this.yAxis | ||
| 431 | + }; | ||
| 432 | + return _.merge(option, this.getDefaultOption()); | ||
| 433 | + } | ||
| 434 | +} | ||
| 0 | \ No newline at end of file | 435 | \ No newline at end of file |
front-end/h5/src/components/plugins/charts/line.js
0 → 100644
| 1 | +import VeLine from 'v-charts/lib/line.common' | ||
| 2 | +import VePie from 'v-charts/lib/pie.common' | ||
| 3 | +import VeHistogram from 'v-charts/lib/histogram.common' | ||
| 4 | +import VeFunnel from 'v-charts/lib/funnel.common' | ||
| 5 | +import PropTypes from '@luban-h5/plugin-common-props' | ||
| 6 | +import 'echarts/lib/component/legend' | ||
| 7 | +import 'echarts/lib/component/markLine' | ||
| 8 | +import 'echarts/lib/component/markPoint' | ||
| 9 | +import 'echarts/lib/component/markArea' | ||
| 10 | +import Parser from '../../../utils/excel-parser' | ||
| 11 | + | ||
| 12 | +export default { | ||
| 13 | + extra: { | ||
| 14 | + defaultStyle: { | ||
| 15 | + width: 320, | ||
| 16 | + height: 400 | ||
| 17 | + } | ||
| 18 | + }, | ||
| 19 | + name: 'lbp-line-chart', | ||
| 20 | + // mixins: [ChartMixin], | ||
| 21 | + props: { | ||
| 22 | + dataset: PropTypes.excel({ | ||
| 23 | + defaultValue: () => [ | ||
| 24 | + ['日期', '销售量'], | ||
| 25 | + ['1月1日', 123], | ||
| 26 | + ['1月2日', 1223], | ||
| 27 | + ['1月3日', 2123], | ||
| 28 | + ['1月4日', 4123], | ||
| 29 | + ['1月5日', 3123], | ||
| 30 | + ['1月6日', 7123] | ||
| 31 | + ] | ||
| 32 | + }), | ||
| 33 | + type: PropTypes.string({ | ||
| 34 | + label: '类型', | ||
| 35 | + defaultValue: 'line', | ||
| 36 | + visible: false | ||
| 37 | + }) | ||
| 38 | + }, | ||
| 39 | + data () { | ||
| 40 | + return { | ||
| 41 | + option: {} | ||
| 42 | + } | ||
| 43 | + }, | ||
| 44 | + render () { | ||
| 45 | + const chartData = Parser.csv2VChartJson(this.dataset) | ||
| 46 | + const settings = { | ||
| 47 | + // metrics: ['日期', '销售量'], | ||
| 48 | + // dimension: ['日期'] | ||
| 49 | + } | ||
| 50 | + switch (this.type) { | ||
| 51 | + case 'line': | ||
| 52 | + return <VeLine data={chartData} settings={settings} /> | ||
| 53 | + case 'histogram': | ||
| 54 | + return <VeHistogram data={chartData} settings={settings} /> | ||
| 55 | + case 'pie': | ||
| 56 | + return <VePie data={chartData} settings={settings} /> | ||
| 57 | + case 'funnel': | ||
| 58 | + return <VeFunnel data={chartData} settings={settings} /> | ||
| 59 | + default: | ||
| 60 | + return null | ||
| 61 | + } | ||
| 62 | + }, | ||
| 63 | + mounted () { | ||
| 64 | + // this.renderChart() | ||
| 65 | + } | ||
| 66 | +} |
front-end/h5/src/utils/excel-parser.js
0 → 100644
| 1 | +/** | ||
| 2 | + * | ||
| 3 | + declare module ExcelRows { | ||
| 4 | + export interface cell { | ||
| 5 | + text: string; | ||
| 6 | + } | ||
| 7 | + export interface Cells { | ||
| 8 | + 0: cell; | ||
| 9 | + 1: cell; | ||
| 10 | + 2: cell; | ||
| 11 | + } | ||
| 12 | + export interface ExcelRows { | ||
| 13 | + cells: Cells; | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * | ||
| 20 | + BinaryMatrix = [ | ||
| 21 | + [any, any, any, ...], | ||
| 22 | + [any, any, any, ...], | ||
| 23 | + [any, any, any, ...], | ||
| 24 | + ] | ||
| 25 | + | ||
| 26 | + ExcelDataType = [ | ||
| 27 | + { | ||
| 28 | + cells: { | ||
| 29 | + 0: { text: any }, | ||
| 30 | + 1: { text: any }, | ||
| 31 | + 2: { text: any } | ||
| 32 | + } | ||
| 33 | + }, | ||
| 34 | + { | ||
| 35 | + cells: { | ||
| 36 | + 0: { text: any }, | ||
| 37 | + 1: { text: any }, | ||
| 38 | + 2: { text: any } | ||
| 39 | + } | ||
| 40 | + }, | ||
| 41 | + ] | ||
| 42 | + */ | ||
| 43 | + | ||
| 44 | +export default class Parser { | ||
| 45 | + /** | ||
| 46 | + * | ||
| 47 | + * @param {*} dataset ExcelDataType | ||
| 48 | + */ | ||
| 49 | + static dataset2excel (dataset) { | ||
| 50 | + return dataset.map(item => ({ | ||
| 51 | + cells: { | ||
| 52 | + 0: { text: item.x }, | ||
| 53 | + 1: { text: item.y }, | ||
| 54 | + 2: { text: item.s } | ||
| 55 | + } | ||
| 56 | + })) | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * | ||
| 61 | + [ | ||
| 62 | + [1,2,3,4], | ||
| 63 | + [5,6,7,8], | ||
| 64 | + [9,10,11,12] | ||
| 65 | + ] | ||
| 66 | + * @param {Object} BinaryMatrix | ||
| 67 | + * @returns {Object} ExcelDataType | ||
| 68 | + */ | ||
| 69 | + static binaryMatrix2excel (binaryMatrix) { | ||
| 70 | + const excelData = binaryMatrix.map((row, rowIndex) => { | ||
| 71 | + // cells: { | ||
| 72 | + // 0: { text: item.x }, | ||
| 73 | + // 1: { text: item.y }, | ||
| 74 | + // 2: { text: item.s } | ||
| 75 | + // } | ||
| 76 | + const cells = {} | ||
| 77 | + row.forEach((cellValue, cellIndex) => { | ||
| 78 | + cells[cellIndex] = { text: cellValue } | ||
| 79 | + }) | ||
| 80 | + return { cells } | ||
| 81 | + }) | ||
| 82 | + return excelData | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + static excel2chartDataSet (excelData) { | ||
| 86 | + const rowsArray = Object.values(excelData.rows).filter(item => typeof item === 'object') | ||
| 87 | + const dataset = rowsArray.map(row => { | ||
| 88 | + const [x, y, s] = Object.values(row.cells).map(item => item.text) | ||
| 89 | + return { | ||
| 90 | + x: x, | ||
| 91 | + y: y, | ||
| 92 | + s: s | ||
| 93 | + } | ||
| 94 | + }) | ||
| 95 | + return dataset | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + static excel2BinaryMatrix (excelData) { | ||
| 99 | + const rowsArray = Object.values(excelData.rows).filter(item => typeof item === 'object') | ||
| 100 | + const dataset = rowsArray.map(row => { | ||
| 101 | + // [1,2,3,4] | ||
| 102 | + const cells = Object.values(row.cells).map(item => item.text) | ||
| 103 | + return cells | ||
| 104 | + }) | ||
| 105 | + console.log('dataset', dataset) | ||
| 106 | + return dataset | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + /** | ||
| 110 | + * | ||
| 111 | + * @param {Array} csvArray | ||
| 112 | + * [ | ||
| 113 | + ['日期', '销售量'], | ||
| 114 | + ["1月1日",123], | ||
| 115 | + ["1月2日",1223], | ||
| 116 | + ["1月3日",2123], | ||
| 117 | + ["1月4日",4123], | ||
| 118 | + ["1月5日",3123], | ||
| 119 | + ["1月6日",7123] | ||
| 120 | + ] | ||
| 121 | + * @returns {Object} | ||
| 122 | + { | ||
| 123 | + columns: ['日期', '销售量'], | ||
| 124 | + rows:[ | ||
| 125 | + { '日期': '1月1日', '销售量': 123 }, | ||
| 126 | + { '日期': '1月2日', '销售量': 1223 }, | ||
| 127 | + { '日期': '1月3日', '销售量': 2123 }, | ||
| 128 | + { '日期': '1月4日', '销售量': 4123 }, | ||
| 129 | + { '日期': '1月5日', '销售量': 3123 }, | ||
| 130 | + { '日期': '1月6日', '销售量': 7123 } | ||
| 131 | + ] | ||
| 132 | + } | ||
| 133 | + */ | ||
| 134 | + static csv2VChartJson (csvArray) { | ||
| 135 | + const columns = csvArray[0] | ||
| 136 | + const rows = csvArray.slice(1) | ||
| 137 | + const json = { | ||
| 138 | + columns, | ||
| 139 | + rows: rows.map((row, index) => { | ||
| 140 | + const obj = {} | ||
| 141 | + columns.forEach((col, colIndex) => { | ||
| 142 | + obj[col] = row[colIndex] | ||
| 143 | + }) | ||
| 144 | + return obj | ||
| 145 | + }) | ||
| 146 | + } | ||
| 147 | + return json | ||
| 148 | + } | ||
| 149 | +} |
front-end/h5/yarn.lock
| @@ -3684,6 +3684,28 @@ ecc-jsbn@~0.1.1: | @@ -3684,6 +3684,28 @@ ecc-jsbn@~0.1.1: | ||
| 3684 | jsbn "~0.1.0" | 3684 | jsbn "~0.1.0" |
| 3685 | safer-buffer "^2.1.0" | 3685 | safer-buffer "^2.1.0" |
| 3686 | 3686 | ||
| 3687 | +echarts-amap@1.0.0-rc.6: | ||
| 3688 | + version "1.0.0-rc.6" | ||
| 3689 | + resolved "https://registry.npm.taobao.org/echarts-amap/download/echarts-amap-1.0.0-rc.6.tgz#5782a74daee52ed44ce3f8f62577561783f09e16" | ||
| 3690 | + integrity sha1-V4KnTa7lLtRM4/j2JXdWF4PwnhY= | ||
| 3691 | + | ||
| 3692 | +echarts-liquidfill@^2.0.2: | ||
| 3693 | + version "2.0.6" | ||
| 3694 | + resolved "https://registry.npm.taobao.org/echarts-liquidfill/download/echarts-liquidfill-2.0.6.tgz#0668dc61d87a6262003090bd32c55aa8108c252e" | ||
| 3695 | + integrity sha1-BmjcYdh6YmIAMJC9MsVaqBCMJS4= | ||
| 3696 | + | ||
| 3697 | +echarts-wordcloud@^1.1.3: | ||
| 3698 | + version "1.1.3" | ||
| 3699 | + resolved "https://registry.npm.taobao.org/echarts-wordcloud/download/echarts-wordcloud-1.1.3.tgz#07b140c8ba76b19c317b43c310f3d5dc99289ff2" | ||
| 3700 | + integrity sha1-B7FAyLp2sZwxe0PDEPPV3Jkon/I= | ||
| 3701 | + | ||
| 3702 | +echarts@^4.8.0: | ||
| 3703 | + version "4.8.0" | ||
| 3704 | + resolved "https://registry.npm.taobao.org/echarts/download/echarts-4.8.0.tgz?cache=0&sync_timestamp=1596214937248&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fecharts%2Fdownload%2Fecharts-4.8.0.tgz#b2c1cfb9229b13d368ee104fc8eea600b574d4c4" | ||
| 3705 | + integrity sha1-ssHPuSKbE9No7hBPyO6mALV01MQ= | ||
| 3706 | + dependencies: | ||
| 3707 | + zrender "4.3.1" | ||
| 3708 | + | ||
| 3687 | editorconfig@^0.15.3: | 3709 | editorconfig@^0.15.3: |
| 3688 | version "0.15.3" | 3710 | version "0.15.3" |
| 3689 | resolved "https://registry.npm.taobao.org/editorconfig/download/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" | 3711 | resolved "https://registry.npm.taobao.org/editorconfig/download/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" |
| @@ -7556,6 +7578,11 @@ number-is-nan@^1.0.0: | @@ -7556,6 +7578,11 @@ number-is-nan@^1.0.0: | ||
| 7556 | resolved "https://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" | 7578 | resolved "https://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" |
| 7557 | integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= | 7579 | integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= |
| 7558 | 7580 | ||
| 7581 | +numerify@1.2.9: | ||
| 7582 | + version "1.2.9" | ||
| 7583 | + resolved "https://registry.npm.taobao.org/numerify/download/numerify-1.2.9.tgz#af4696bb1d57f8d3970a615d8b0cd53d932bd559" | ||
| 7584 | + integrity sha1-r0aWux1X+NOXCmFdiwzVPZMr1Vk= | ||
| 7585 | + | ||
| 7559 | nwsapi@^2.0.7: | 7586 | nwsapi@^2.0.7: |
| 7560 | version "2.2.0" | 7587 | version "2.2.0" |
| 7561 | resolved "https://registry.npm.taobao.org/nwsapi/download/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" | 7588 | resolved "https://registry.npm.taobao.org/nwsapi/download/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" |
| @@ -9208,6 +9235,11 @@ reselect@^3.0.1: | @@ -9208,6 +9235,11 @@ reselect@^3.0.1: | ||
| 9208 | resolved "https://registry.npm.taobao.org/reselect/download/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" | 9235 | resolved "https://registry.npm.taobao.org/reselect/download/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" |
| 9209 | integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= | 9236 | integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= |
| 9210 | 9237 | ||
| 9238 | +resize-detector@^0.2.2: | ||
| 9239 | + version "0.2.2" | ||
| 9240 | + resolved "https://registry.npm.taobao.org/resize-detector/download/resize-detector-0.2.2.tgz#b207e72912bef0bda9fb825fe894ed9686ca965e" | ||
| 9241 | + integrity sha1-sgfnKRK+8L2p+4Jf6JTtlobKll4= | ||
| 9242 | + | ||
| 9211 | resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: | 9243 | resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: |
| 9212 | version "1.5.1" | 9244 | version "1.5.1" |
| 9213 | resolved "https://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" | 9245 | resolved "https://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" |
| @@ -10681,6 +10713,11 @@ utila@^0.4.0, utila@~0.4: | @@ -10681,6 +10713,11 @@ utila@^0.4.0, utila@~0.4: | ||
| 10681 | resolved "https://registry.npm.taobao.org/utila/download/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" | 10713 | resolved "https://registry.npm.taobao.org/utila/download/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" |
| 10682 | integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= | 10714 | integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= |
| 10683 | 10715 | ||
| 10716 | +utils-lite@0.1.10: | ||
| 10717 | + version "0.1.10" | ||
| 10718 | + resolved "https://registry.npm.taobao.org/utils-lite/download/utils-lite-0.1.10.tgz#d2908c0482e23c31e6b082558540e7134ffad7d7" | ||
| 10719 | + integrity sha1-0pCMBILiPDHmsIJVhUDnE0/619c= | ||
| 10720 | + | ||
| 10684 | utils-merge@1.0.1: | 10721 | utils-merge@1.0.1: |
| 10685 | version "1.0.1" | 10722 | version "1.0.1" |
| 10686 | resolved "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" | 10723 | resolved "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" |
| @@ -10691,6 +10728,17 @@ uuid@^3.0.1, uuid@^3.3.2: | @@ -10691,6 +10728,17 @@ uuid@^3.0.1, uuid@^3.3.2: | ||
| 10691 | resolved "https://registry.npm.taobao.org/uuid/download/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" | 10728 | resolved "https://registry.npm.taobao.org/uuid/download/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" |
| 10692 | integrity sha1-RWjwIW54dg7h2/Ok0s9T4iQRKGY= | 10729 | integrity sha1-RWjwIW54dg7h2/Ok0s9T4iQRKGY= |
| 10693 | 10730 | ||
| 10731 | +v-charts@^1.19.0: | ||
| 10732 | + version "1.19.0" | ||
| 10733 | + resolved "https://registry.npm.taobao.org/v-charts/download/v-charts-1.19.0.tgz#07b701800b159bd514264ffc8bf12b0405142da3" | ||
| 10734 | + integrity sha1-B7cBgAsVm9UUJk/8i/ErBAUULaM= | ||
| 10735 | + dependencies: | ||
| 10736 | + echarts-amap "1.0.0-rc.6" | ||
| 10737 | + echarts-liquidfill "^2.0.2" | ||
| 10738 | + echarts-wordcloud "^1.1.3" | ||
| 10739 | + numerify "1.2.9" | ||
| 10740 | + utils-lite "0.1.10" | ||
| 10741 | + | ||
| 10694 | v-click-outside@^2.1.4: | 10742 | v-click-outside@^2.1.4: |
| 10695 | version "2.1.4" | 10743 | version "2.1.4" |
| 10696 | resolved "https://registry.npm.taobao.org/v-click-outside/download/v-click-outside-2.1.4.tgz#7d7813b09a0fd4a9d0fbccc2ca12ca7c5591e087" | 10744 | resolved "https://registry.npm.taobao.org/v-click-outside/download/v-click-outside-2.1.4.tgz#7d7813b09a0fd4a9d0fbccc2ca12ca7c5591e087" |
| @@ -11459,3 +11507,8 @@ yorkie@^2.0.0: | @@ -11459,3 +11507,8 @@ yorkie@^2.0.0: | ||
| 11459 | is-ci "^1.0.10" | 11507 | is-ci "^1.0.10" |
| 11460 | normalize-path "^1.0.0" | 11508 | normalize-path "^1.0.0" |
| 11461 | strip-indent "^2.0.0" | 11509 | strip-indent "^2.0.0" |
| 11510 | + | ||
| 11511 | +zrender@4.3.1: | ||
| 11512 | + version "4.3.1" | ||
| 11513 | + resolved "https://registry.npm.taobao.org/zrender/download/zrender-4.3.1.tgz?cache=0&sync_timestamp=1596718864550&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fzrender%2Fdownload%2Fzrender-4.3.1.tgz#baf8aa6dc8187a2f819692d7d5f9bedfa2b90fa3" | ||
| 11514 | + integrity sha1-uviqbcgYei+BlpLX1fm+36K5D6M= |