UNPKG

hd-echarts

Version:

huidian ECharts component for Vue.js.

474 lines (453 loc) 13.3 kB
import echarts from 'echarts/lib/echarts' import debounce from 'lodash/debounce' import { addListener, removeListener } from 'resize-detector' import { legendIcons } from './constant' // enumerating ECharts events for now const EVENTS = [ 'legendselectchanged', 'legendselected', 'legendunselected', 'legendscroll', 'datazoom', 'datarangeselected', 'timelinechanged', 'timelineplaychanged', 'restore', 'dataviewchanged', 'magictypechanged', 'geoselectchanged', 'geoselected', 'geounselected', 'pieselectchanged', 'pieselected', 'pieunselected', 'mapselectchanged', 'mapselected', 'mapunselected', 'axisareaselected', 'focusnodeadjacency', 'unfocusnodeadjacency', 'brush', 'brushselected', 'rendered', 'finished', 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu' ] const INIT_TRIGGERS = ['theme', 'initOptions', 'autoresize', 'series'] const REWATCH_TRIGGERS = ['manualUpdate', 'watchShallow'] export default { props: { title: Object, legend: Object, tooltip: Object, radar: [Array, Object], xAxis: [Array, Object], yAxis: [Array, Object], series: [Array, Object], options: { type: Object, default () { return {} } }, theme: { type: [String, Object], default: function () { return 'default' } }, initOptions: { type: Object, default () { return {} } }, height: String, group: String, autoresize: { type: Boolean, default: true }, watchShallow: Boolean, manualUpdate: Boolean }, data () { return { lastArea: 0, style: { height: '400px', width: '100%' }, _options: null, yAxisIsObject: true } }, watch: { group (group) { this.chart.group = group } }, methods: { initInitOptions () { this.initOptions.width && (this.style.width = this.initOptions.width) this.initOptions.height && (this.style.height = this.initOptions.height) this.height && (this.style.height = this.height) }, mergeNormalOptions (options) { this.title && (options.title = this.title) this.legend && (options.legend = this.legend) this.tooltip && (options.tooltip = this.tooltip) this.radar && (options.radar = this.radar) this.xAxis && (options.xAxis = this.xAxis) this.yAxis && (options.yAxis = this.yAxis) this.series && (options.series = this.series) return this.deepClone(options) }, /** * 自动格式化yAxisLabel * 值超过10000时自动格式化成1.0万,带一位小数 */ formatterYAxisAxisLabel (options) { if (!options.yAxis) return options let yAxis = options.yAxis const setFormatter = function (yAxis) { if (!yAxis) return if (!(yAxis && yAxis.axisLabel && yAxis.axisLabel.formatter)) { !yAxis.axisLabel && (yAxis.axisLabel = {}) yAxis.axisLabel.formatter = function (v) { if (Number.isInteger(v)) { let symbol = '', value, k = 10000, sizes = ['', '万', '亿', '万亿', '亿亿', 'Infinite'], i if (v < 0) { symbol = '-' value = - v } else { value = v } if (value < k) { return v } else { i = Math.floor(Math.log(value) / Math.log(k)) return symbol + ((value / Math.pow(k, i))).toFixed(1) + sizes[i] } } else { return v } } } } this.yAxisIsObject = Object.prototype.toString.call(options.yAxis) === '[object Object]' if (this.yAxisIsObject) { setFormatter(yAxis) } else { yAxis.forEach(yAxi => { setFormatter(yAxi) }) } return options }, /** * 极坐标type会是line或bar */ isBarOrLine () { let series= this._options.series if(Object.prototype.toString.call(series) === '[object Array]') { return series.some(elem => { return elem.type === 'line' || elem.type === 'bar' }) } else { return series.type === 'line' || series.type === 'bar' } }, /** * 获取xAxis.data的长度 * xAxis是object or array */ getXAxisDataLength () { let options = this._options if (options.xAxis !== undefined) { if (Object.prototype.toString.call(options.xAxis) === '[object Object]') { return options.xAxis.data !== undefined ? options.xAxis.data.length : null } else { return Math.max( (options.xAxis[0].data !== undefined ? options.xAxis[0].data.length : 0), (options.xAxis[1] !== undefined && options.xAxis[1].data !== undefined ? options.xAxis[1].data.length : 0) ) } } return null }, // provide a explicit merge option method mergeOptions (options, notMerge, lazyUpdate) { if (this.manualUpdate) { this.manualOptions = options } if (!this.chart) { this.init() } else { this.delegateMethod('setOption', options, notMerge, lazyUpdate) } }, // just delegates ECharts methods to Vue component // use explicit params to reduce transpiled size for now appendData (params) { this.delegateMethod('appendData', params) }, resize (options) { this.delegateMethod('resize', options) }, dispatchAction (payload) { this.delegateMethod('dispatchAction', payload) }, convertToPixel (finder, value) { return this.delegateMethod('convertToPixel', finder, value) }, convertFromPixel (finder, value) { return this.delegateMethod('convertFromPixel', finder, value) }, containPixel (finder, value) { return this.delegateMethod('containPixel', finder, value) }, showLoading (type, options) { this.delegateMethod('showLoading', type, options) }, hideLoading () { this.delegateMethod('hideLoading') }, getDataURL (options) { return this.delegateMethod('getDataURL', options) }, getConnectedDataURL (options) { return this.delegateMethod('getConnectedDataURL', options) }, clear () { this.delegateMethod('clear') }, dispose () { this.delegateMethod('dispose') }, delegateMethod (name, ...args) { if (!this.chart) { this.init() } return this.chart[name](...args) }, delegateGet (methodName) { if (!this.chart) { this.init() } return this.chart[methodName]() }, getArea () { return this.$el.offsetWidth * this.$el.offsetHeight }, // 实现legend.icon和legend.data.icon自定义配置 modifyLegend (options) { const legend = options.legend if (!legend || !echarts.customOptions || !echarts.customOptions.legend) return options const customIcon = echarts.customOptions.legend.icon if (customIcon !== undefined) { const icon = legend.icon if (icon !== undefined && !legendIcons.includes(icon) && customIcon[icon]) { legend.icon = customIcon[icon] } if (legend.data) { legend.data.forEach(item => { if (Object.prototype.toString.call(item) === '[object Object]') { const icon = item.icon if (icon !== undefined && !legendIcons.includes(icon) && customIcon[icon]) { item.icon = customIcon[icon] } } }) } } return options }, modifyOptions (opts) { let options = this.formatterYAxisAxisLabel(opts) options = this.modifyLegend(options) return options }, //使用递归的方式实现数组、对象的深拷贝 deepClone (obj) { var objClone = Array.isArray(obj) ? [] : {}; if (obj && typeof obj === "object") { for (let key in obj) { if (obj.hasOwnProperty(key)) { if (obj[key] && typeof obj[key] === "object") { objClone[key] = this.deepClone(obj[key]); } else { objClone[key] = obj[key]; } } } } return objClone; }, init () { if (this.chart) { return } let chart = echarts.init(this.$el, this.theme, this.initOptions) if (this.group) { chart.group = this.group } this._options = this.mergeNormalOptions(this.manualOptions || this.options) this.setType && this.setType() this._options = this.modifyOptions(this._options) chart.setOption(this._options || {}, true) // expose ECharts events as custom events EVENTS.forEach(event => { chart.on(event, params => { this.$emit(event, params) }) }) if (this.autoresize) { this.lastArea = this.getArea() this.__resizeHandler = debounce(() => { if (this.lastArea === 0) { // emulate initial render for initially hidden charts this.mergeOptions({}, true) this.resize() this.mergeOptions(this.options || this.manualOptions || {}, true) } else { this.resize() } this.lastArea = this.getArea() }, 100, { leading: true }) addListener(this.$el, this.__resizeHandler) } Object.defineProperties(this, { // Only recalculated when accessed from JavaScript. // Won't update DOM on value change because getters // don't depend on reactive values width: { configurable: true, get: () => { return this.delegateGet('getWidth') } }, height: { configurable: true, get: () => { return this.delegateGet('getHeight') } }, isDisposed: { configurable: true, get: () => { return !!this.delegateGet('isDisposed') } }, computedOptions: { configurable: true, get: () => { return this.delegateGet('getOption') } } }) this.chart = chart }, initOptionsWatcher () { if (this.__unwatchOptions) { this.__unwatchOptions() this.__unwatchOptions = null } if (!this.manualUpdate) { this.__unwatchOptions = this.$watch('options', (val, oldVal) => { if (!this.chart && val) { this.init() } else { // mutating `options` will lead to merging // replacing it with new reference will lead to not merging // eg. // `this.options = Object.assign({}, this.options, { ... })` // will trigger `this.chart.setOption(val, true) // `this.options.title.text = 'Trends'` // will trigger `this.chart.setOption(val, false)` this.chart.setOption(this.modifyOptions(val), val !== oldVal) } }, { deep: !this.watchShallow }) } }, destroy () { if (this.autoresize) { removeListener(this.$el, this.__resizeHandler) } this.dispose() this.chart = null }, refresh () { if (this.chart) { this.destroy() this.init() } } }, created () { this.initOptionsWatcher() if (this.initOptions) { this.initInitOptions() } INIT_TRIGGERS.forEach(prop => { this.$watch(prop, () => { this.refresh() }, { deep: true }) }) REWATCH_TRIGGERS.forEach(prop => { this.$watch(prop, () => { this.initOptionsWatcher() this.refresh() }) }) }, mounted () { // auto init if `options` is already provided if (this.options) { this.init() } }, activated () { if (this.autoresize) { this.chart && this.chart.resize() } }, beforeDestroy () { if (!this.chart) { return } this.destroy() }, connect (group) { if (typeof group !== 'string') { group = group.map(chart => chart.chart) } echarts.connect(group) }, disconnect (group) { echarts.disConnect(group) }, registerMap (mapName, geoJSON, specialAreas) { echarts.registerMap(mapName, geoJSON, specialAreas) }, registerTheme (name, theme) { echarts.registerTheme(name, theme) }, graphic: echarts.graphic }