dt-charts
Version:
基于echarts封装的vue组件,配置项与数据分离,简少代码量
264 lines (248 loc) • 7.74 kB
JavaScript
/*
* @Description: DtCharts 用于渲染echarts,1.接收每个seriesItem 2.处理成option 3.将prop传递的额外组件替换option对应的组件 4.设置option
优先级:this.$dtChartsGlobalOpt < this.defOpt < this.option < setting
* @Author: yanxiao
* @Github: https://github.com/yanxiaos
* @Date: 2021-12-22 15:03:56
* @LastEditors: yanxiao
*/
import * as echarts from "echarts";
import Loading from '../../components/loading'
import DataEmpty from '../../components/data-empty'
import {
deepMergeObj,
mergeObject,
mergeArray
} from "../../utils/utils";
import { debounce } from "../../utils/debounce";
import {
isObject, isArray,
isExist
} from "../../utils/getType";
import {
DEFAULT_THEME,
ECHARTS_SETTINGS
} from '../../constants'
export default {
name: 'DtCharts',
componentName: 'DtCharts',
// render 函数若存在,则 Vue 构造函数不会从 template 选项或通过 el 选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。
render (h) {
return h('div', {
// class: [camelToKebab(this.$options.name || this.$options._componentTag)],
style: this.canvasStyle,
}, [
// 渲染 canvas
h('div', {
style: this.canvasStyle,
// 空状态或者加载中状态 添加遮罩层class
class: {
'dt-charts': true,
'v-charts-mask-status': this.dataEmpty || this.loading
},
ref: 'canvas'
}),
// 空状态
h(DataEmpty, {
style: { display: this.dataEmpty ? '' : 'none' }
}),
// 加载中状态
h(Loading, {
style: { display: this.loading ? '' : 'none' }
}),
// 插槽
this.$slots.default
])
},
props: {
// 初始化图表init时的配置项
initOptions: { type: Object, default () { return {} } },
// setOption时的配置项
setOptionOpts: { type: Object, default () { return {} } },
// 宽
width: { type: String, default: '' },
// 高
height: { type: String, default: '' },
// 空状态
dataEmpty: Boolean,
// 加载中状态
loading: Boolean,
// 主题: json配置文件或注册过的主题名
theme: [Object, String],
// 单个组件的默认配置
defOpt: { type: Object, default: () => { } },
// 是否开启resize,默认开启
resizeable: { type: Boolean, default: true },
// resize处理间隔
resizeDelay: { type: Number, default: 0 },
// 子组件发生改变后处理option的间隔
changeDelay: { type: Number, default: 0 },
},
computed: {
canvasStyle () {
return {
width: this.width,
height: this.height,
position: 'relative'
}
},
},
created () {
// 处理option的防抖函数
this.handlerOption = debounce(this.optionHandler, this.changeDelay, 11)
// resize的防抖函数
// this.resizeHandler = debounce(this.resize, this.resizeDelay)
// 监听子组件数据变化
this.$on('handleChild', this.handleChild)
// 监听组件销毁
this.$on('hiddenChild', this.hiddenChild)
},
mounted () {
// 初始化echarts
this.init()
this.handlerOption({ notMerge: true })
},
beforeDestroy () {
this.clean()
},
data () {
return {
echarts: null,
option: {},
optionObj: {}
}
},
watch: {
// 主题
theme: {
deep: true,
handler: 'themeChange'
},
$attrs: {
deep: true,
handler () {
this.handlerOption({ notMerge: true })
}
},
// 是否开启resize,默认开启
resizeable: 'resizeableHandler'
},
methods: {
// 子组件数据变化
handleChild (option, componentId) {
// 相同子组件新数据覆盖之前的数据
this.optionObj[componentId] = option
this.option = {}
// 不同子组件的option合并为一个option
for (const key in this.optionObj) {
this.option = this.mergeOption(this.option, this.optionObj[key])
}
// 处理option
this.handlerOption({ notMerge: false })
},
// option合并(配置项中的 数组差量合并)
mergeOption (option, diffOption) {
let result = {}
for (const opt in diffOption) {
if (isObject(diffOption[opt])) {
result[opt] = mergeObject(option[opt] || {}, diffOption[opt])
} else if (isArray(diffOption[opt])) {
// 数组合并--series数组拼接,其他配置进行数组合并(因为:例如xAxis有多个重复的配置只需要一个,series)
if (opt === 'series') {
result[opt] = [...option[opt] || [], ...diffOption[opt]]
} else {
// 其他配置只有不一样时才会拼接,如果一样会覆盖
result[opt] = mergeArray(option[opt] || [], diffOption[opt])
}
} else {
result[opt] = diffOption[opt]
}
}
return { ...option, ...result }
},
// 处理option 优先级:defOpt < this.option < setting
optionHandler ({ notMerge }) {
// 1. 设置默认option
let defOpt = deepMergeObj(this.$dtChartsGlobalOpt, this.defOpt, notMerge)
this.option = deepMergeObj(defOpt, this.option, notMerge)
// 2. 设置用户prop传递的option
for (let i = 0; i < ECHARTS_SETTINGS.length; i++) {
let opItem = ECHARTS_SETTINGS[i]
let setting = this.$attrs[opItem]
if (isExist(setting)) {
this.option[opItem] = deepMergeObj(this.option[opItem], setting, notMerge)
}
}
// 返回option
this.setOption(this.option)
},
// 子组件隐藏
hiddenChild (componentId) {
// 相同子组件新数据覆盖之前的数据
delete this.optionObj[componentId]
this.option = {}
// 不同子组件的option合并为一个option
for (const key in this.optionObj) {
this.option = this.mergeOption(this.option, this.optionObj[key])
}
// 处理option
this.handlerOption({ notMerge: true })
},
// 初始化echarts
init () {
// 如果图表存在则直接退出
if (this.echarts) return
// 主题 优先级: 主题名 ->全局主题 -> 默认主题
const theme = this.theme || this.$dtChartsGlobalTheme || DEFAULT_THEME
this.echarts = echarts.init(this.$refs.canvas, theme, this.initOptions)
// 启用resize
if (this.resizeable) this.addResizeListener()
},
// 主题发生改变
themeChange () {
this.$emit('themeChange')
// 销毁图表
this.clean()
// 重新初始化
this.init()
},
// 是否开启resize,watch发生改变,重新设置
resizeableHandler (resizeable) {
resizeable ? this.addResizeListener() : this.removeResizeListener()
},
// 窗口尺寸发生变化重新设置echarts尺寸
resize () {
if (this.echarts) {
this.echarts.resize()
this.$emit('resize')
}
},
// 监听resize
addResizeListener () {
window.addEventListener('resize', this.resizeHandler)
},
// 删除resize
removeResizeListener () {
window.removeEventListener('resize', this.resizeHandler)
},
// 销毁图表
clean () {
// 如果开启了resiz,则删除removeResizeListener
if (this.resizeable) this.removeResizeListener()
// 调用图表销毁方法
this.echarts.dispose()
this.echarts = null
},
// 设置配置
setOption (options) {
this.echarts.setOption(options, {
notMerge: true,
...this.setOptionOpts
})
this.$emit('setOption', this.option)
},
getOption () {
return this.option
}
}
}