<script lang="ts" setup name="BarChartVertical"> /** * 垂直条形图,支持渐变,支持增加背景颜色 */ import * as echarts from 'echarts/core' import type { ECharts } from 'echarts' import { init } from 'echarts' import type { Ref } from 'vue' import type { ECBasicOption } from 'echarts/types/dist/shared' import type { barOption, barSeriesOption, lineDataI } from './echart-interface' import tdTheme from './theme.json' // 引入默认主题 const props = defineProps({ /** * id */ id: { type: String, default: 'chart', }, minValue: { type: Number, default: null, }, maxValue: { type: Number, default: null, }, isDataZoom: { type: Boolean, default: true, }, yType: { type: Boolean, default: true, }, /** * 标题 */ title: { type: String, default: '', }, /** * 加载状态 */ loading: { type: Boolean, default: false, }, /** * 网格配置 */ grid: { type: Object, default: () => { return { top: 50, left: 20, right: 20, bottom: 10, containLabel: true, // 是否包含坐标轴的刻度标签 } }, }, /** * 图例设置对象 */ legend: { type: Object, default: () => { return { show: true, icon: 'circle', orient: 'vertile', // 图例方向 align: 'left', // 图例标记和文本的对齐,默认自动 top: 20, right: 60, itemWidth: 12, itemHeight: 12, textStyle: { color: '#96989b', }, padding: [0, 0, 0, 120], } }, }, /** * 图例列表,非必传 */ legendData: { type: Array, default: () => { return [] }, }, /** * 图表宽 */ width: { type: String, default: '100%', }, /** * 图表高 */ height: { type: String, default: '100%', }, /** * X轴刻度数据,格式为['周一','周二'...] */ xAxisData: { type: Array, default: () => { return [] }, }, /** * 数据,格式为[{name:'系列名',data:[0,0,0,...0], color:'可选'},...] */ data: { type: Array, default: () => { return [] }, }, /** * 固定纵轴最大值,非必填 */ min: { type: [Number, String], default: 0, }, max: { type: [Number, String], default: 4095, }, /** * 单位 */ unit: { type: String, default: '', }, /** * 调色盘取色策略: series按照系列,data按照数据项 */ colorBy: { type: String, default: 'series', }, /** * 柱子颜色,渐变, * 若colorBy为data, 格式[['柱1颜色1','柱1颜色2','柱1颜色3'],['柱2颜色1','柱2颜色2','柱2颜色3']] * 若colorBy为series, 格式[['柱1颜色1','柱1颜色2','柱1颜色3']] */ colors: { type: Array, default: () => { return [ ['#ffff00', '#ffff00'], ['#ff0000', '#ff0000'], ['#fed700', '#fff'], ['#feb501', '#fff'], ] }, }, /** * 柱子背景颜色 */ backgroundColor: { type: String, default: '', }, /** * 柱子圆角 */ barConer: { type: [Number, Array], default: 0, }, /** * 柱子宽度 */ barWidth: { type: [Number, String], default: 1, }, // 是否显示X轴线 showXAxis: { type: Boolean, default: true, }, // 是否显示Y轴线 showYAxis: { type: Boolean, default: false, }, // 是否显示Y轴线 showYSplitLine: { type: Boolean, default: true, }, /** * 轴线颜色 */ axisLineColor: { type: String, default: '#96989b', }, /** * 轴线上文字颜色 */ fontColor: { type: String, default: '#96989b', }, // 轴线文本宽度 axisLabelWidth: { type: [Number, String], default: '', }, /** * 是否显示标签 */ showLabel: { type: Boolean, default: true, }, /** * 图形上文本标签颜色 */ labelColor: { type: String, default: '#96989b', }, /** * 标签位置,top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight */ labelPosition: { type: String, default: 'top', }, /** * 标签格式化 */ labelFormatter: { type: String, default: '{c}', }, /** * 是否为渐变柱状图 */ gradient: { type: Boolean, default: false, }, }) // 图表对象 let chart: ECharts const chartRef: Ref<HTMLElement | null> = ref(null) // 监听数据变化 watch( [() => props.xAxisData, props.data], ([newName, newNums]) => { refreshChart() if(props.minValue !== null) { refreshArea() } }, { immediate: true, deep: true, }, ) watch(() => props.max, () => { refreshChart() }, { immediate: true, }) watch(() => props.maxValue, () => { refreshArea() }, { immediate: true, }) watch(() => props.minValue, () => { refreshArea() }, { immediate: true, }) // 构建option function buildOption() { const zoom = [{ show: false, realtime: true, start: 0, end: 100, right: '23', // 距离容器右侧侧的距离 filterMode: 'none' }, { type: 'inside', realtime: true, start: 0, end: 100, }, { show: false, type: 'slider', yAxisIndex: 0, filterMode: 'none', }, { type: 'inside', yAxisIndex: 0, filterMode: 'none', } ] const option: barOption = { title: [{ text: props.title }], grid: props.grid, legend: props.legend, // 图例 dataZoom: props.isDataZoom ? (props.minValue !== null ? zoom.slice(0,2) : zoom) : [], // 缩略轴 tooltip: { trigger: 'axis', // textStyle: { // fontSize: 16, // }, axisPointer: { type: 'cross', axis: 'x', label: { fontSize: 13, }, }, valueFormatter: (value: string | number) => { return value + props.unit }, }, // 提示框 yAxis: [ { name: props.unit, type: props.yType ? 'value': 'log', boundaryGap: true, axisLine: { show: props.showYAxis, lineStyle: { color: props.axisLineColor, // 轴线的颜色 }, }, nameTextStyle: { // 坐标轴名称的文字样式 color: props.fontColor, fontSize: '60%', verticalAlign: 'middle', }, axisLabel: { show: true, color: props.fontColor, // y轴名称颜色 fontSize: 14, }, splitLine: { show: props.showYSplitLine, lineStyle: { color: ['#7a6b74'], type: 'dashed', }, }, }, ], xAxis: [ { // type: props.yType ? 'value': 'log', type: 'value', min: props.min, max: props.max, boundaryGap: true, axisLine: { show: props.showXAxis, lineStyle: { color: props.axisLineColor, // 轴线的颜色 }, }, axisLabel: { color: props.fontColor, fontSize: 15, showMaxLabel: true, // 显示最右侧的标签 }, splitLine: { show: false, }, }, ], series: [] as barSeriesOption[], toolbox: props.isDataZoom ? {}: { feature: { brush: { type: ['lineX'], title: { lineX: '横向选择', } } } }, brush: props.isDataZoom ? {}: { toolbox: ['lineX'], xAxisIndex: 0, defaultBrush: true, }, selectedMode: 'multiple', } // 标题 if (props.title) { option.title = { show: false, text: props.title, } } // 图例 if (props.legend && props.legendData.length > 0) { option.legend!.data = props.legendData } // 横轴数据 if (props.xAxisData && props.xAxisData.length > 0) { if (Array.isArray(option.xAxis) && option.xAxis.length > 0) { option.xAxis[0].data = props.xAxisData // 横轴, 水平柱状图,y轴为横轴 } } // 轴线文本宽度 if (props.axisLabelWidth && Array.isArray(option.xAxis) && option.xAxis.length > 0) { option.xAxis[0].axisLabel!.width = props.axisLabelWidth } // // 如果有最大值规定,固定最大值 // if (props.max && Array.isArray(option.yAxis) && option.yAxis.length > 0) { // option.yAxis[0].max = props.max // } // 数据 if (props.data) { const newSeries: barSeriesOption[] = [] // 遍历data, 拆分柱状图名称,数据和颜色 for (let itemIndex = 0; itemIndex < props.data.length; itemIndex++) { const item = props.data[itemIndex] const series: barSeriesOption = { name: item.name, type: 'bar', colorBy: props.colorBy, animation: false, showBackground: !!props.backgroundColor, backgroundStyle: { color: props.backgroundColor, borderRadius: Array.isArray(props.barConer) ? props.barConer as number[] : props.barConer, }, itemStyle: { borderRadius: Array.isArray(props.barConer) ? props.barConer as number[] : props.barConer, }, data: item.data, zlevel: item.z, z: item.z, } // 如果规定了柱宽度 if (props.barWidth) { series.barWidth = props.barWidth } // 如果要固定显示在图标右边 if (props.labelPosition == 'fixedEnd' && chartRef.value?.clientWidth) { let chartWidth = chartRef.value?.clientWidth - props.grid.left || 0 - props.grid.right || 0 if (props.axisLabelWidth && typeof props.axisLabelWidth === 'number') { chartWidth = chartWidth - props.axisLabelWidth } else { chartWidth = chartWidth - 80 } series.label.normal!.position = [chartWidth, '50%'] } series.itemStyle!.color = props.colors[itemIndex][0] newSeries.push(series) } option.series = newSeries } return option } // 初始化图表 function initChart() { chart = init(chartRef.value as HTMLElement, tdTheme) chart.setOption({}) refreshChart() } function getChart() { return chart } defineExpose({ getChart }) // 刷新图表 function refreshChart() { if (chart) { const option = buildOption() chart.setOption(option as unknown as ECBasicOption,false) } } function refreshArea() { if(props.minValue === null || props.maxValue === null) return if (chart) { var dataZoomOption = chart.getOption().dataZoom; chart.setOption({ series: [{ markArea: { label: { color: 'rgba(255, 255, 255, 1)' }, itemStyle: { color: 'rgb(68, 76, 159, 0.4)' }, data: [ [ {name: props.minValue!.toFixed(2) + ' -- ' + props.maxValue!.toFixed(2), xAxis: props.minValue,}, {xAxis: props.maxValue,} ], ] } }] }) // 重新渲染图表,并将之前获取的 dataZoom 配置传入 chart.setOption({ dataZoom: dataZoomOption }) } } window.addEventListener('resize', () => { chart.resize() }) onMounted(() => { initChart() }) </script> <template> <div :id="id" ref="chartRef" v-loading="loading" class="chart" :style="{ height, width }" /> </template> <style lang="scss" scoped> .chart { width: 100%; height: 100%; } </style>