<script lang="ts" setup name="BarChartVertical3D"> /** * 垂直条形3d */ 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', }, /** * 标题 */ title: { type: String, default: '', }, /** * 标题样式 */ titleTextStyle: { type: Object, default: () => { return { color: '#333', fontStyle: 'normal', fontWeight: 'bolder', fontSize: 14, lineHeight: 56, } }, }, /** * 加载状态 */ 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: 5, right: 20, itemWidth: 12, itemHeight: 12, padding: [0, 0, 0, 120], } }, }, /** * 图例列表,非必传 */ legendData: { type: Array<string>, default: () => { return [] }, }, /** * 图表宽 */ width: { type: String, default: '100%', }, /** * 图表高 */ height: { type: String, default: '100%', }, /** * X轴刻度数据,格式为['周一','周二'...] */ xAxisData: { type: Array<string>, default: () => { return [] }, }, /** * 数据,格式为[0,0,0,...0] */ data: { type: Array<lineDataI>, default: () => { return [] }, }, /** * 固定纵轴最大值,非必填 */ max: { type: [Number, String], default: '', }, /** * 单位 */ 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 [ ['#2d8cf0', '#fff'], ['#2352d6', '#fff'], ['#fed700', '#fff'], ['#feb501', '#fff'], ] }, }, /** * 柱子背景颜色 */ backgroundColor: { type: String, default: '', }, /** * 柱子圆角 */ barConer: { type: [Number, Array], default: 50, }, /** * 柱子宽度 */ barWidth: { type: [Number, String], default: '', }, // 是否显示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: '#000000', }, // 轴线文本宽度 axisLabelWidth: { type: [Number, String], default: '', }, /** * 是否显示标签 */ showLabel: { type: Boolean, default: true, }, /** * 图形上文本标签颜色 */ labelColor: { type: String, default: '#3d7eff', }, /** * 标签位置,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: true, }, }) // 图表对象 let chart: ECharts const chartRef: Ref<HTMLElement | null> = ref(null) // 监听数据变化 watch( [() => props.xAxisData, props.data], ([newName, newNums], [oldName, oldNums]) => { // initChart() refreshChart() }, { immediate: true, deep: true, }, ) watch(() => props, () => { // initChart() refreshChart() }, { deep: true, }) // 构建option function buildOption() { const option: barOption = { grid: props.grid, legend: props.legend, // 图例 tooltip: { trigger: 'axis', textStyle: { fontSize: 16, }, axisPointer: { type: 'cross', label: { fontSize: 13, }, }, valueFormatter: (value: string | number) => { return value ? value + props.unit : value === 0 ? '0' : '' }, }, // 提示框 xAxis: { type: 'category', data: props.xAxisData, axisLine: { show: false, lineStyle: { color: '#ccc', }, }, // offset: 25, axisTick: { show: false, length: 9, alignWithLabel: true, lineStyle: { color: '#7DFFFD', }, }, axisLabel: { show: true, fontSize: 12, color: '#fff', // rotate: 45 }, }, yAxis: [ { min: 0, max: Math.max(...props.data.map((item: any) => Number(item || '0'))), // interval: 200, type: 'value', // name: '%', axisLine: { show: false, lineStyle: { color: '#ccc', }, }, splitLine: { show: true, lineStyle: { type: 'solid', color: 'rgba(255,255,255,0.4)', }, }, axisTick: { show: false, }, axisLabel: { show: true, fontSize: 12, color: '#fff', }, boundaryGap: ['20%', '20%'], }, ], series: [] as barSeriesOption[], } // 标题 if (props.title) { option.title = { show: true, text: props.title, textStyle: props.titleTextStyle, padding: 0, top: -16, } } // 图例 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轴为横轴 // option.xAxis[1].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[] = [ { type: 'custom', renderItem: (params: any, api: any) => { const location = api.coord([api.value(0), api.value(1)]) var color = api.value(1) > 10000 ? 'red' : new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#5cc4eb', }, { offset: 0.8, color: '#21658c', }, ]) // console.log(color) return { type: 'group', children: [ { type: 'CubeLeft', shape: { api, xValue: api.value(0), yValue: api.value(1), x: location[0], y: location[1], xAxisPoint: api.coord([api.value(0), 0]), }, style: { fill: (color = api.value(1) > 10000 ? 'red' : new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#5cc4eb', }, { offset: 0.8, color: '#21658c', }, ])), }, }, { type: 'CubeRight', shape: { api, xValue: api.value(0), yValue: api.value(1), x: location[0], y: location[1], xAxisPoint: api.coord([api.value(0), 0]), }, style: { fill: (color = api.value(1) > 10000 ? 'red' : new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#048fd4', }, { offset: 0.8, color: '#195684', }, ])), }, }, { type: 'CubeTop', shape: { api, xValue: api.value(0), yValue: api.value(1), x: location[0], y: location[1], xAxisPoint: api.coord([api.value(0), 0]), }, style: { fill: (color = api.value(1) > 10000 ? 'red' : new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#65c7ec', }, { offset: 1, color: '#65c7ec', }, ])), }, }, ], } }, data: props.data, }, ] option.series = newSeries } return option } // 初始化图表 function initChart() { chart = init(chartRef.value as HTMLElement, tdTheme) chart.setOption({}) // 绘制左侧面 const CubeLeft = echarts.graphic.extendShape({ shape: { x: 0, y: 0, }, buildPath(ctx, shape) { const xAxisPoint = shape.xAxisPoint const c0 = [shape.x, shape.y] const c1 = [shape.x - 8, shape.y - 8] const c2 = [xAxisPoint[0] - 8, xAxisPoint[1] - 8] const c3 = [xAxisPoint[0], xAxisPoint[1]] ctx.moveTo(c0[0], c0[1]).lineTo(c1[0], c1[1]).lineTo(c2[0], c2[1]).lineTo(c3[0], c3[1]).closePath() }, }) // 绘制右侧面 const CubeRight = echarts.graphic.extendShape({ shape: { x: 0, y: 0, }, buildPath(ctx, shape) { const xAxisPoint = shape.xAxisPoint const c1 = [shape.x, shape.y] const c2 = [xAxisPoint[0], xAxisPoint[1]] const c3 = [xAxisPoint[0] + 13, xAxisPoint[1] - 4] const c4 = [shape.x + 13, shape.y - 4] ctx.moveTo(c1[0], c1[1]).lineTo(c2[0], c2[1]).lineTo(c3[0], c3[1]).lineTo(c4[0], c4[1]).closePath() }, }) // 绘制顶面 const CubeTop = echarts.graphic.extendShape({ shape: { x: 0, y: 0, }, buildPath(ctx, shape) { const c1 = [shape.x, shape.y] const c2 = [shape.x + 13, shape.y - 4] const c3 = [shape.x + 5, shape.y - 12] const c4 = [shape.x - 8, shape.y - 8] ctx.moveTo(c1[0], c1[1]).lineTo(c2[0], c2[1]).lineTo(c3[0], c3[1]).lineTo(c4[0], c4[1]).closePath() }, }) // 注册三个面图形 echarts.graphic.registerShape('CubeLeft', CubeLeft) echarts.graphic.registerShape('CubeRight', CubeRight) echarts.graphic.registerShape('CubeTop', CubeTop) } // 刷新图表 function refreshChart() { if (chart) { const option = buildOption() chart.setOption(option as unknown as ECBasicOption, true) } } window.addEventListener('resize', () => { chart.resize() }) onMounted(() => { initChart() refreshChart() }) </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>