主要思路
- 定义一个echarts图标,数据为空,image采用base64编码
- 图标宽高根据父宽高自适应
- 渲染echarts函数,切换清除图例
- 定义暂无数据指令
定义option
/**
* 暂无数据
* @param {number} width
* @param {number} height
* @returns option
*/
function emptyChartOptionFn(width = 150, height = 120) {
return {
title: {
text: `{a|}\n{b|暂无数据}`,
x: 'center',
y: 'center',
itemGap: 0,
textStyle: {
rich: {
a: {
height,
width,
backgroundColor: {
image:
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTU1IiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDE1NSAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIG9wYWNpdHk9IjAuNiI+CjxyZWN0IHg9IjgyLjU0MyIgeT0iMTIiIHdpZHRoPSI3MCIgaGVpZ2h0PSI5MCIgcng9IjYiIHRyYW5zZm9ybT0icm90YXRlKDYuMDg2NjYgODIuNTQzIDEyKSIgZmlsbD0iI0Y1RjZGQSIgc3Ryb2tlPSIjOEY5QUIyIi8+CjxyZWN0IG9wYWNpdHk9IjAuMiIgeD0iOTMuMzQ5IiB5PSIzMy4yNjU2IiB3aWR0aD0iMTMiIGhlaWdodD0iMiIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKDYuMDg2NjYgOTMuMzQ5IDMzLjI2NTYpIiBmaWxsPSIjOEY5QUIyIi8+CjxyZWN0IHg9IjExMC4yNTMiIHk9IjM1LjA2ODQiIHdpZHRoPSIyNyIgaGVpZ2h0PSIyIiByeD0iMSIgdHJhbnNmb3JtPSJyb3RhdGUoNi4wODY2NiAxMTAuMjUzIDM1LjA2ODQpIiBmaWxsPSIjOEY5QUIyIi8+CjxyZWN0IHg9IjkxLjg2NDYiIHk9IjQ3LjE4NjUiIHdpZHRoPSIzMiIgaGVpZ2h0PSIyIiByeD0iMSIgdHJhbnNmb3JtPSJyb3RhdGUoNi4wODY2NiA5MS44NjQ2IDQ3LjE4NjUpIiBmaWxsPSIjOEY5QUIyIi8+CjxyZWN0IG9wYWNpdHk9IjAuMiIgeD0iMTI3LjY2MiIgeT0iNTEuMDAzOSIgd2lkdGg9IjgiIGhlaWdodD0iMiIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKDYuMDg2NjYgMTI3LjY2MiA1MS4wMDM5KSIgZmlsbD0iIzhGOUFCMiIvPgo8cmVjdCBvcGFjaXR5PSIwLjIiIHg9Ijg4Ljg5NTYiIHk9Ijc1LjAyODgiIHdpZHRoPSIzMi42ODg0IiBoZWlnaHQ9IjIiIHJ4PSIxIiB0cmFuc2Zvcm09InJvdGF0ZSg2LjA4NjY2IDg4Ljg5NTYgNzUuMDI4OCkiIGZpbGw9IiM4RjlBQjIiLz4KPHJlY3QgeD0iOTAuMzgwMSIgeT0iNjEuMTA3OSIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIiIHJ4PSIxIiB0cmFuc2Zvcm09InJvdGF0ZSg2LjA4NjY2IDkwLjM4MDEgNjEuMTA3OSkiIGZpbGw9IiM4RjlBQjIiLz4KPHJlY3Qgb3BhY2l0eT0iMC4yIiB4PSIxMTQuMjQ1IiB5PSI2My42NTI4IiB3aWR0aD0iMjAiIGhlaWdodD0iMiIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKDYuMDg2NjYgMTE0LjI0NSA2My42NTI4KSIgZmlsbD0iIzhGOUFCMiIvPgo8L2c+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2RfMjA0MF82NjU5OCkiPgo8cmVjdCB4PSIxNy4zMzQ0IiB5PSIxMDIuNzEiIHdpZHRoPSI3MCIgaGVpZ2h0PSIxMDAiIHJ4PSI2IiB0cmFuc2Zvcm09InJvdGF0ZSgtOTcuNjYzMSAxNy4zMzQ0IDEwMi43MSkiIGZpbGw9IiNGNUY2RkEiIHN0cm9rZT0iIzhGOUFCMiIvPgo8Y2lyY2xlIG9wYWNpdHk9IjAuMiIgY3g9IjMzLjU5NjciIGN5PSI0OC40MjQzIiByPSI4IiB0cmFuc2Zvcm09InJvdGF0ZSgtNy42NjMwOSAzMy41OTY3IDQ4LjQyNDMpIiBzdHJva2U9IiM3QTg1OTkiIHN0cm9rZS13aWR0aD0iMS40Ii8+CjxwYXRoIGQ9Ik0zNC42NjM0IDU2LjM1MjhDMzYuMzYyMSA1Ni4xMjQzIDM3Ljk0MjQgNTUuMzU2MiAzOS4xNzE3IDU0LjE2MThDNDAuNDAwOSA1Mi45Njc0IDQxLjIxNCA1MS40MDk3IDQxLjQ5MTMgNDkuNzE4NEM0MS43Njg1IDQ4LjAyNyA0MS40OTUzIDQ2LjI5MTMgNDAuNzExNyA0NC43NjY5QzM5LjkyODEgNDMuMjQyNSAzOC42NzU3IDQyLjAxMDEgMzcuMTM4OSA0MS4yNTEyQzM1LjYwMjEgNDAuNDkyMyAzMy44NjIyIDQwLjI0NyAzMi4xNzU1IDQwLjU1MTVDMzAuNDg4OCA0MC44NTYgMjguOTQ0NSA0MS42OTQxIDI3Ljc3IDQyLjk0MjRDMjYuNTk1NiA0NC4xOTA4IDI1Ljg1MzEgNDUuNzgzMyAyNS42NTE5IDQ3LjQ4NTRDMjUuNDUwOCA0OS4xODc1IDI1LjgwMTYgNTAuOTA5MiAyNi42NTI3IDUyLjM5NjkiIHN0cm9rZT0iIzhGOUFCMiIgc3Ryb2tlLXdpZHRoPSIyIi8+CjxjaXJjbGUgb3BhY2l0eT0iMC4yIiBjeD0iMzYuMjYzNyIgY3k9IjY4LjI0NTYiIHI9IjUiIHRyYW5zZm9ybT0icm90YXRlKC03LjY2MzA5IDM2LjI2MzcgNjguMjQ1NikiIHN0cm9rZT0iIzdBODU5OSIgc3Ryb2tlLXdpZHRoPSIxLjQiLz4KPHBhdGggZD0iTTM2LjkzMDQgNzMuMjAxQzM3LjkxMDUgNzMuMDY5MSAzOC44Mjk1IDcyLjY0OTYgMzkuNTcxMSA3MS45OTU0QzQwLjMxMjcgNzEuMzQxMyA0MC44NDM4IDcwLjQ4MTkgNDEuMDk3IDY5LjUyNkM0MS4zNTAyIDY4LjU3IDQxLjMxNDMgNjcuNTYwNSA0MC45OTM3IDY2LjYyNUM0MC42NzMyIDY1LjY4OTUgNDAuMDgyNSA2NC44NyAzOS4yOTYyIDY0LjI3MDJDMzguNTA5OSA2My42NzA1IDM3LjU2MzUgNjMuMzE3MyAzNi41NzY1IDYzLjI1NTRDMzUuNTg5NSA2My4xOTM2IDM0LjYwNjQgNjMuNDI1OCAzMy43NTE0IDYzLjkyMjZDMzIuODk2NCA2NC40MTk1IDMyLjIwNzkgNjUuMTU4OCAzMS43NzMgNjYuMDQ3QzMxLjMzODIgNjYuOTM1MSAzMS4xNzY1IDY3LjkzMjMgMzEuMzA4MyA2OC45MTI0IiBzdHJva2U9IiM4RjlBQjIiIHN0cm9rZS13aWR0aD0iMiIvPgo8Y2lyY2xlIG9wYWNpdHk9IjAuMiIgY3g9IjM4LjUzMDUiIGN5PSI4NS4wOTM4IiByPSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNy42NjMwOSAzOC41MzA1IDg1LjA5MzgpIiBzdHJva2U9IiM3QTg1OTkiIHN0cm9rZS13aWR0aD0iMS40Ii8+CjxwYXRoIGQ9Ik00My40ODU5IDg0LjQyN0M0My4zODkgODMuNzA3MSA0My4xMzY0IDgzLjAxNjkgNDIuNzQ1NyA4Mi40MDQ1QzQyLjM1NSA4MS43OTIxIDQxLjgzNTUgODEuMjcyMSA0MS4yMjM0IDgwLjg4MDlDNDAuNjExMyA4MC40ODk3IDM5LjkyMTQgODAuMjM2NSAzOS4yMDE1IDgwLjEzOUMzOC40ODE2IDgwLjA0MTUgMzcuNzQ5MiA4MC4xMDIxIDM3LjA1NTEgODAuMzE2NEMzNi4zNjEgODAuNTMwOCAzNS43MjE5IDgwLjg5MzggMzUuMTgyNCA4MS4zODAzQzM0LjY0MjkgODEuODY2NyAzNC4yMTU4IDgyLjQ2NDkgMzMuOTMxIDgzLjEzMzJDMzMuNjQ2MSA4My44MDE0IDMzLjUxMDMgODQuNTIzNyAzMy41MzMgODUuMjQ5OEMzMy41NTU2IDg1Ljk3NTkgMzMuNzM2MiA4Ni42ODgzIDM0LjA2MjIgODcuMzM3NSIgc3Ryb2tlPSIjOEY5QUIyIiBzdHJva2Utd2lkdGg9IjIiLz4KPHJlY3Qgb3BhY2l0eT0iMC4yIiB4PSI1Ny4wMDA0IiB5PSI1Ny4zODM4IiB3aWR0aD0iMzgiIGhlaWdodD0iMiIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKC03LjY2MzA5IDU3LjAwMDQgNTcuMzgzOCkiIGZpbGw9IiM3QTg1OTkiLz4KPHJlY3Qgb3BhY2l0eT0iMC4yIiB4PSI2MS4wMDA5IiB5PSI4Ny4xMTU3IiB3aWR0aD0iMzgiIGhlaWdodD0iMiIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKC03LjY2MzA5IDYxLjAwMDkgODcuMTE1NykiIGZpbGw9IiM3QTg1OTkiLz4KPHJlY3Qgb3BhY2l0eT0iMC4yIiB4PSI1Ny4wMzk5IiB5PSIzNS4xODAyIiB3aWR0aD0iMiIgaGVpZ2h0PSIyMSIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKC03LjY2MzA5IDU3LjAzOTkgMzUuMTgwMikiIGZpbGw9IiM3QTg1OTkiLz4KPHJlY3Qgb3BhY2l0eT0iMC4yIiB4PSI1OC4wNjcxIiB5PSI2NS4zMTIiIHdpZHRoPSIyIiBoZWlnaHQ9IjI0IiByeD0iMSIgdHJhbnNmb3JtPSJyb3RhdGUoLTcuNjYzMDkgNTguMDY3MSA2NS4zMTIpIiBmaWxsPSIjN0E4NTk5Ii8+CjxyZWN0IG9wYWNpdHk9IjAuMiIgeD0iNjYuOTUwNyIgeT0iMzMuODQ2NyIgd2lkdGg9IjIiIGhlaWdodD0iMjEiIHJ4PSIxIiB0cmFuc2Zvcm09InJvdGF0ZSgtNy42NjMwOSA2Ni45NTA3IDMzLjg0NjcpIiBmaWxsPSIjN0E4NTk5Ii8+CjxyZWN0IG9wYWNpdHk9IjAuMiIgeD0iNzYuODYxMyIgeT0iMzIuNTEzMiIgd2lkdGg9IjIiIGhlaWdodD0iMjEiIHJ4PSIxIiB0cmFuc2Zvcm09InJvdGF0ZSgtNy42NjMwOSA3Ni44NjEzIDMyLjUxMzIpIiBmaWxsPSIjN0E4NTk5Ii8+CjxyZWN0IG9wYWNpdHk9IjAuMiIgeD0iODYuNzcyMSIgeT0iMzEuMTc5NyIgd2lkdGg9IjIiIGhlaWdodD0iMjEiIHJ4PSIxIiB0cmFuc2Zvcm09InJvdGF0ZSgtNy42NjMwOSA4Ni43NzIxIDMxLjE3OTcpIiBmaWxsPSIjN0E4NTk5Ii8+CjxyZWN0IHg9IjU4Ljc3MzQiIHk9IjQ4LjA2NCIgd2lkdGg9IjIiIGhlaWdodD0iOCIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKC03LjY2MzA5IDU4Ljc3MzQgNDguMDY0KSIgZmlsbD0iIzhGOUFCMiIvPgo8cmVjdCB4PSI2Ny40ODQiIHk9IjM3LjgxMSIgd2lkdGg9IjIiIGhlaWdodD0iMTciIHJ4PSIxIiB0cmFuc2Zvcm09InJvdGF0ZSgtNy42NjMwOSA2Ny40ODQgMzcuODExKSIgZmlsbD0iIzhGOUFCMiIvPgo8cmVjdCB4PSI3OC4xOTQ4IiB5PSI0Mi40MjM4IiB3aWR0aD0iMiIgaGVpZ2h0PSIxMSIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKC03LjY2MzA5IDc4LjE5NDggNDIuNDIzOCkiIGZpbGw9IiM4RjlBQjIiLz4KPHJlY3QgeD0iODYuOTA1NCIgeT0iMzIuMTcwOSIgd2lkdGg9IjIiIGhlaWdodD0iMjAiIHJ4PSIxIiB0cmFuc2Zvcm09InJvdGF0ZSgtNy42NjMwOSA4Ni45MDU0IDMyLjE3MDkpIiBmaWxsPSIjOEY5QUIyIi8+CjxwYXRoIGQ9Ik02NC41NjUgODMuNjA4OUw3MS4xMzk3IDczLjQzMkw4NS41MzQ4IDgwLjc4NzVMOTIuMzQ3NCA3Ni4wNjk0TDk1LjAwMzQgNjIuMzYwMyIgc3Ryb2tlPSIjOEY5QUIyIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L2c+CjxkZWZzPgo8ZmlsdGVyIGlkPSJmaWx0ZXIwX2RfMjA0MF82NjU5OCIgeD0iMC4yNDU3MjgiIHk9IjEyLjI0NjEiIHdpZHRoPSIxMjcuOTUiIGhlaWdodD0iMTAyLjIxOCIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgo8ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPgo8ZmVDb2xvck1hdHJpeCBpbj0iU291cmNlQWxwaGEiIHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIgcmVzdWx0PSJoYXJkQWxwaGEiLz4KPGZlT2Zmc2V0IGR4PSIyIiBkeT0iMiIvPgo8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSI1Ii8+CjxmZUNvbXBvc2l0ZSBpbjI9ImhhcmRBbHBoYSIgb3BlcmF0b3I9Im91dCIvPgo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMC4xNSAwIi8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93XzIwNDBfNjY1OTgiLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3dfMjA0MF82NjU5OCIgcmVzdWx0PSJzaGFwZSIvPgo8L2ZpbHRlcj4KPC9kZWZzPgo8L3N2Zz4K',
},
},
b: {
verticalAlign: 'top',
fontSize: 12,
lineHeight: 18,
color: '#686E7C',
},
},
},
subtextStyle: {
fontSize: 12,
},
},
}
}
自适配宽高
const getEmptyWH = el => {
let width = 150
let height = 120
// 宽度不够
if (el.clientWidth < 150) {
let radio = 120 / 150
width = el.clientWidth
height = width * radio
}
// 高度不够
if (el.clientHeight < 120) {
let radio = 150 / 120
height = el.clientHeight - 20
width = height * radio
}
return {
width,
height,
}
}
渲染echarts图标至el
const emptyChartFn = el => {
//初始化echart
let myChart = Vue.prototype.$echarts.init(el, 'light', {
height: 'auto',
width: 'auto',
})
const { width, height } = getEmptyWH(el)
myChart.setOption(optionFn(width, height))
// myChart绑定到el上,方便后续调用
el.myChart = myChart
el.showLoading = myChart.showLoading
el.hideLoading = myChart.hideLoading
// 监听屏幕变化
el.debounceResize = debounce(() => {
const { width, height } = getEmptyWH(el)
// 清空当前实例
el.myChart.clear()
// 重新绘制
el.myChart.setOption(optionFn(width, height))
// 必须加上resize,否则不起作用
el.myChart.resize({
animation: {
duration: 200,
},
})
}, 500)
// window.addEventListener('resize', el.debounceResize)
const observer = new ResizeObserver(el.debounceResize)
observer.observe(el)
el._observer = observer
}
定义暂无数据指令
// 暂无数据指令
const emptyChart = {
inserted(el, binding, vnode) {
const isEmpty = binding.value
if (!isEmpty) {
return
}
Vue.nextTick(() => {
emptyChartFn(el)
})
},
update(el, binding, vnode) {
const isEmpty = binding.value
if (isEmpty) {
let myChart = el?.myChart
if (!myChart) {
emptyChartFn(el)
} else {
myChart.clear()
emptyChartFn(el)
}
}
},
//指令卸载的时候去除window事件监听
unbind(el, binding, vnode) {
// window.removeEventListener('resize', el.debounceResize)
const observer = el._observer
if (observer) {
observer.unobserve(el)
delete el._observer
}
},
}
注册指令
Vue.directive('emptyChart', emptyChart)
使用
<div v-empty-chart="isEmpty" class="h-full pt-6"></div>