需要实现的效果如下
<script setup lang="ts" name="RepsSingleBarChart">
import * as echarts from 'echarts'
import { getInitecharts } from '@/utils/echart'
// 定义 props 类型
interface Props {
id: string
dataObj: Record<string, unknown> | null
}
// 定义 SeriesOption 类型
interface SeriesOption {
type: string
data: number[]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
custom?: any // 允许自定义属性
renderItem?: (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
api: any,
) => {
type: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
children?: any[]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
shape?: Record<string, any>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
style?: Record<string, any>
silent?: boolean
}
encode?: {
x: number
y: number
}
}
// 定义 ECharts 配置项类型
interface EChartsOption {
tooltip?: echarts.TooltipComponentOption
grid?: echarts.GridComponentOption
xAxis?: echarts.XAXisComponentOption | echarts.XAXisComponentOption[]
yAxis?: echarts.YAXisComponentOption | echarts.YAXisComponentOption[]
series?: SeriesOption | SeriesOption[]
title?: echarts.TitleComponentOption
[key: string]: unknown
}
// 定义 props
const props = defineProps<Props>()
// 定义 ECharts 实例类型
let myChart: echarts.ECharts | null = null
// 定义 ECharts 配置项
const option = ref<EChartsOption>({
tooltip: {
show: false,
},
grid: {
top: '31px',
left: '17px',
right: '21px',
bottom: '9px',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: ['2025.01', '2025.02', '2025.03', '2025.04', '2025.05', '2025.06'],
axisTick: {
alignWithLabel: true,
show: false,
},
axisLine: {
show: true, // 确保显示轴线
lineStyle: {
color: 'rgba(153,204,255,0.50)',
type: 'solid', // 设置为实线
width: 1,
},
},
axisLabel: {
color: '#DAECFF',
fontSize: 14,
fontFamily: 'Arial, Arial-400',
fontWeight: 400,
lineHeight: 36,
},
// 添加X轴网格线配置
splitLine: {
show: true, // 不显示X轴方向的网格线
lineStyle: {
color: 'rgba(153,204,255,0.20)',
type: 'dashed',
width: 1,
},
},
},
],
yAxis: [
{
type: 'value',
axisLabel: {
color: 'rgba(218,236,255,0.60)',
fontSize: 14,
fontFamily: 'Arial, Arial-400',
fontWeight: 400,
lineHeight: 20,
},
// Y轴轴线样式
axisLine: {
show: false,
},
// Y轴刻度线样式
axisTick: {
show: false,
},
// Y轴网格线样式(设置为虚线)
splitLine: {
show: true,
lineStyle: {
color: 'rgba(153,204,255,0.20)', // 浅色虚线
type: 'dashed', // 虚线样式
dashOffset: 2, // 虚线偏移量
width: 1,
},
},
},
],
series: [
{
type: 'custom',
data: [10, 0, 200, 334, 390, 330],
encode: {
// 将类别和值映射到维度
x: 0,
y: 1,
},
renderItem: (_, api) => {
// api.value(0)/params.dataIndex // 当前下标
// api.value(1) // 当前下标对应的值
// api.coord([api.value(0), api.value(1)]) // 当前下标对应的值的XY坐标,对应的是中心点
// api.coord([api.value(0), 0]) // 底部的XY坐标
// 小长方形之间的间隔
const gap = 4
// 小长方形宽度
const segmentWidth = 22
// 小长方形高度
const segmentHeight = 6
// 底部的XY坐标
const base = api.coord([api.value(0), 0])
// 数值的XY坐标
const value = api.coord([api.value(0), api.value(1)])
// 计算需要多少个小长方形
let segmentCount = Math.round((base[1] - value[1]) / (segmentHeight + gap))
// 数值
const number = api.value(1)
// 最大的Y坐标
let maxY = 0
if (segmentCount === 0 && number) {
segmentCount = 1
}
let children = []
for (let i = 0; i < segmentCount; i++) {
maxY = base[1] - segmentHeight - i * (segmentHeight + gap)
children.push({
type: 'rect',
shape: {
x: value[0] - segmentWidth / 2, // 居中
y: maxY, // 从下往上排列
width: segmentWidth,
height: segmentHeight,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#00d4ff' },
{ offset: 1, color: '#00aaff' },
]),
},
})
}
if (segmentCount === 0) {
children = [
{
type: 'rect',
shape: {
x: value[0] - segmentWidth / 2, // 居中
y: base[1] - 1, // 从下往上排列
width: segmentWidth,
height: 1,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#00d4ff' },
{ offset: 1, color: '#00aaff' },
]),
},
},
{
type: 'text',
style: {
text: number.toString(), // 显示数值
fill: '#FFFFFF', // 文字颜色
fontSize: 14,
fontFamily: 'Arial, Arial-700',
fontWeight: '700',
lineHeight: 24,
textAlign: 'center',
},
position: [
value[0], // x位置(居中)
base[1] - 24, // y位置
],
z: 10, // 确保文本在最上层
},
]
} else {
// 添加数值文本(在柱子顶部)
children.push({
type: 'text',
style: {
text: number.toString(), // 显示数值
fill: '#FFFFFF', // 文字颜色
fontSize: 14,
fontFamily: 'Arial, Arial-700',
fontWeight: '700',
lineHeight: 24,
textAlign: 'center',
},
position: [
value[0], // x位置(居中)
maxY - segmentHeight * 3 - 5, // y位置(顶部上方5px)
],
z: 10, // 确保文本在最上层
})
}
return {
type: 'group',
children,
silent: true, // 禁用交互
}
},
},
],
})
// 定义空数据时的提示信息
const emptyObj = reactive<Record<string, string>>({
dashboardPtglRzxxs: '暂无入驻学校',
})
/**
* @description: 渲染
* @return {void}
*/
const getRender = () => {
if (myChart) {
myChart.dispose()
}
myChart = getInitecharts(echarts, props.id)
nextTick(() => {
if (!myChart) return
if (props.dataObj && Object.keys(props.dataObj).length > 0) {
myChart.setOption(option.value)
} else {
myChart.setOption({
// 禁用所有交互
tooltip: {
show: false,
},
graphic: {
elements: [
{
type: 'group',
left: 'center',
top: 'center',
silent: true, // 关键设置:禁用交互
children: [
{
type: 'image',
style: {
image: '/src/assets/img/table-empty.png',
width: 88,
height: 88,
},
y: 0,
},
{
type: 'text',
style: {
text: emptyObj[props.id],
fontSize: 14,
fontWeight: '400',
fill: 'rgba(255, 255, 255, .8)',
opacity: '.8',
fontFamily: 'Microsoft YaHei UI, Microsoft YaHei UI-400',
lineHeight: 24,
letterSpacing: 0.07,
},
y: 88 + 4, // 图片高度 + 间隔
x: 2,
},
],
},
],
},
})
}
})
}
watch(
() => props.dataObj,
() => {
getRender()
},
{ deep: true },
)
onMounted(() => {
getRender()
})
</script>
<template>
<div :id="props.id" class="bar"></div>
</template>
<style lang="less" scoped>
.bar {
width: 100%;
height: 100%;
}
</style>
封装echarts统一调用
import * as echarts from 'echarts'
/**
* @description: 初始化echarts
* @param {typeof echarts} echartsInstance echarts实例
* @param {string} id dom的id
* @param {('canvas' | 'svg')} renderer 渲染方式
* @returns {echarts.ECharts} echarts实例
*/
export function getInitecharts(
echartsInstance: typeof echarts,
id: string,
renderer: 'canvas' | 'svg' = 'canvas',
): echarts.ECharts {
const dom = document.getElementById(id)
if (!dom) {
throw new Error(`Element with id "${id}" not found`)
}
return echartsInstance.init(dom, null, {
renderer,
useDirtyRect: false,
})
}