echarts二级菜单柱状图

1. 需求

  • 将普通柱状图表改成能够显示二级菜单销售量的图表
    1. 原图:
      在这里插入图片描述
    2. 修改后的图形
      在这里插入图片描述

2. 解决方法

01 | 使用字符串拼接法生成图表
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <title>Document</title>

    <style>
		.histogram_box{
			margin: 0 auto;
			margin-top: 69px;
			width: 878px;
		}
		.header{
			height: 52px;
			line-height: 51px;
		}
		.header .span1{
			width: 6px;
			height: 20px;
			background: #2FAFEC;
			display: inline-block;
		}
		.header .title{
			font-size: 18px;
			font-family: Arial-BoldMT;
			font-weight: bold;
			line-height: 47px;
			color: #333333;
			display: inline-block;
		}
		.header .botton1,.botton2,.select{
			display: inline-block;
			float: right;
			margin-left: 20px;
			padding:0 3px;
			background: #FFFFFF;
			border: 2px solid #CCCCCC;
			opacity: 1;
			border-radius: 7px;
			line-height: 36px;
			text-align: center;
			cursor: pointer;
		}
		.header .botton2{
			border: 2px solid #2FAFEC;
			border-radius: 7px;
			color: #2FAFEC;
		}
		.histogram{
			width: 878px;
			height: auto;
			background: #FFFFFF;
			border: 1px solid #E6E6E6;
			border-radius: 24px;
			position: relative;
			padding-bottom: 60px;
		}
		.his_cont{
			position: relative;
			margin-left: 36px;
			z-index: 99;
			padding-top: 50px;
		}
		.his_item{
			position: relative;
			height: 5px;
			line-height: 20px;
			vertical-align:middle;
			margin-bottom: 30px;
		}
		.arrows{
			width:0;
			height:0; 
			font-size:0;
			border-width:8px;
			border-style:solid;
			border-color:#6AAACE transparent transparent;
			overflow:hidden; 
			transform: rotate(-90deg);
			z-index: 99;
			display: inline-block;
			cursor: pointer;
			vertical-align:middle;
		}
		.arrows_active{
			transform: rotate(0deg);
            
		}
		.his_title{
			width: 188px;
			/* height: 40px; */
			color: #6AAACE;
			font-size: 15px;
			text-align: right;
			display: inline-block;
			vertical-align:middle;
		}
		.his_width{
			height: 16px;
			background: #2FAFEC;
			opacity: 0.88;
			margin-left: 10px;
			margin-right: 12px;
			display: inline-block;
			vertical-align:middle;
		}
		.his_number{
			width: 37px;
			height: 19px;
			line-height: 19px;
			font-size: 17px;
			font-family: ArialMT;
			color: #2FAFEC;
			opacity: 1;
			display: inline-block;
			vertical-align:middle;
		}
		.his_item_list{
			display: none;
		}
		.his_item_item{
			position: relative;
			padding-left: 15px;
			margin-bottom: 30px;
			height: 5px;
		}
		.his_item_item .his_width{
			background: #808080;

		}
		
		.his_bg{
			width: 592px;
			height: 100%;
			position: absolute;
			right: 36px;
			/* background-color: #E6E6E6; */
			z-index: 0;
			/* border-bottom: 1px solid #E6E6E6; */
			border-bottom: 1px transparent black;
            
		}
		.his_bg_item{
			width: 0px;
			height: 100%;
			border: 1px solid #E6E6E6;
			margin-right: 50px;
			float: left;
			position: relative;
		}
		.his_xtext{
			position: absolute;
			bottom: -30px;
			left: -17px;
			text-align: left;
            color: #6AAACE;
		}
    </style>
</head>
<body>
    <div class="histogram_box" id="histogram_box">
        
        <div class="histogram" id="bar">
            <div class="his_cont" id="his_cont">
                <div class="his_bg">
                    <div class="his_bg_item"><div class="his_xtext" style="left: -3px;"></div></div>
                    <div class="his_bg_item" style="opacity: 0;"><div class="his_xtext" style="opacity: 0;"></div></div>
                    <div class="his_bg_item"><div class="his_xtext"></div></div>
                    <div class="his_bg_item" style="opacity: 0;"><div class="his_xtext" style="opacity: 0;"></div></div>
                    <div class="his_bg_item"><div class="his_xtext" ></div></div>
                    <div class="his_bg_item" style="opacity: 0;"><div class="his_xtext" style="opacity: 0;"></div></div>
                    <div class="his_bg_item"><div class="his_xtext"></div></div>
                    <div class="his_bg_item" style="opacity: 0;"><div class="his_xtext" style="opacity: 0;"></div></div>
                    <div class="his_bg_item"><div class="his_xtext" ></div></div>
                    <div class="his_bg_item" style="opacity: 0;"><div class="his_xtext" style="opacity: 0;"></div></div>
                    <div class="his_bg_item"><div class="his_xtext"></div></div>
                </div>
                <div id="bar_box">
                </div>
            </div>
        </div>
    </div>

    <script>
        let barList=[
			{
				"name": "服饰",
				"value": 6000,
                "percent": "60%",
                "childNode": [
                    {
                    "name": "服饰1",
                    "value": 3000,
                    "percent": "50%"
                    },
                    {
                    "name": "服饰2",
                    "value": 2000,
                    "percent": "50%"
                    },
                    {
                    "name": "服饰3",
                    "value": 1000,
                    "percent": "50%"
                    },
                ]
			},
			{
				"name": "电子产品",
				"value": 4000,
                "percent": "60%",
                "childNode": [
                    {
                    "name": "电子产品1",
                    "value": 1000,
                    "percent": "50%"
                    },
                    {
                    "name": "电子产品2",
                    "value": 1500,
                    "percent": "50%"
                    },
                    {
                    "name": "电子产品3",
                    "value": 2500,
                    "percent": "50%"
                    },
                ]
			},
			{
				"name": "图书",
				"value": 3000,
                "percent": "60%"
			}
		
		]


        let numlist=barList.map(item=>{
            return item.value
        })
        let max=Math.ceil(Math.max(...numlist))//获取数组最大值
        let step
        if(max<=10){//获取标签步数
            step=1
        }else if(max<=100){
            step=Math.ceil(max/10)
        }else if(max<=1000){
            step=Math.ceil(max/100)*10
        }else if(max<=10000){
            step=Math.ceil(max/1000)*100
        }else if(max<=100000){
            step=Math.ceil(max/10000)*1000
        }else{
            step=Math.ceil(max/10)
        }

        let titleboxList=document.querySelectorAll('.his_xtext');//获取所有x轴的标签 
        for(let i=0,s=0;i<=10;s=s+step,i++){//循环生成x轴的标签
            titleboxList[i].innerText=s
        }
        
  
        let bar_box=document.getElementById('bar_box')
        let strBar=''
        for(let c=0;c<barList.length;c++){
            strBar+='<div class="his_list">'
            strBar+='<div class="his_item" οnclick="isShow(his_item_list'+(c+1)+',arr'+(c+1)+')">'
            strBar+='<div class="arrows" id="arr'+(c+1)+'"></div>'
            strBar+='<div class="his_title">'+barList[c].name+'</div><div class="his_width" style="width:'+barList[c].value/titleboxList[10].innerText*520+'px; background: -webkit-linear-gradient(left,#FFFFFF,#44A1FF); "></div><div class="his_number">'+barList[c].value+'</div></div>'
            strBar+='<div class="his_item_list" id="his_item_list'+(c+1)+'">'
            if(barList[c].childNode!=undefined){
                for(let m=0;m<barList[c].childNode.length;m++){
                    strBar+='<div class="his_item_item">'
                    strBar+='<div class="his_title" style="color: #AEAEAE;">'+barList[c].childNode[m].name+'</div><div class="his_width" style="width:'+barList[c].childNode[m].value/titleboxList[10].innerText*520+'px; background: -webkit-linear-gradient(left,#FFFFFF,#9B9B9B); "></div><div class="his_number">'+barList[c].childNode[m].value+'</div>'
                    strBar+='</div>'
                }
            }
            strBar+='</div>'				
            strBar+='</div>'
        }
        bar_box.innerHTML=strBar

        //是否折叠
		function isShow(oDiv,oArr){
		  oDiv.style.display = oDiv.style.display=='block'?'none':'block',
		  oArr.className = oArr.className=='arrows arrows_active'?'arrows':'arrows arrows_active'
		
        }



    </script>

</body>
</html>


  • 效果图:
    在这里插入图片描述
02 | 使用Echarts方法生成
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<!-- <script type="text/javascript" src="./echarts.js"></script> -->
<!-- <script src="https://cdn.bootcss.com/echarts/3.7.1/echarts.min.js"></script> -->
<script src="https://cdn.bootcss.com/echarts/5.0.2/echarts.min.js"></script>

<body>

    <div id="main" style="width: auto;"></div>

</body>

<script type="text/javascript">
    /**
     * 核心思想就是将每一项的开合状态记录到close对象中
     * 在将每一项的bar的子级元素都完整添加到data数据中 哪怕会有冗余
     * 在根据被点击的对象位置获取其中的children 添加到给echarts显示的对象中
     * 在关闭bar时就根据close状态来判断数量
     * 然后再用splice去除echarts显示的对象中的元素
     */
     let response = {
         "color": ["#6AAACE", "#808080", "#FFA500", "#681752"],
         "data": [{
                 "name": "服饰",
                 "percent": "60",
                 "id": '1',
                 "children": [{
                         "name": "服饰1",
                         "id": '1-1',
                         "percent": "40"
                     },
                     {
                        "name": "服饰2",
                         "id": '1-2',
                         "percent": "50",
                         "children": [{
                                 "name": "31",
                                 "id": '1-2-1',
                                 "percent": "80"
                             },
                            {
                                 "name": "32",
                                "id": '1-2-2',
                                 "percent": "70",
                                 "children": [{
                                         "name": "we",
                                         "id": '1-2-2-1',
                                         "percent": "120"
                                     },
                                     {
                                         "name": "qq",
                                         "id": '1-2-2-2',
                                         "percent": "10"
                                     },
                                     {
                                         "name": "af",
                                         "id": '1-2-2-3',
                                         "percent": "70"
                                     }
                                 ]
                             },
                             {
                                 "name": "33",
                                 "id": '1-2-3',
                                 "percent": "10"
                             }
                         ]
                     },
                     {
                         "name": "服饰3",
                         "id": '1-3',
                         "percent": "70"
                     }
                ]
             },
             {
                "name": "电子产品",
                 "id": '2',
                 "percent": "30",
                 "children": [{
                         "name": "服饰21",
                         "id": '2-1',
                         "percent": "40"
                     },
                     {
                         "name": "服饰22",
                         "id": '2-2',
                         "percent": "50"
                     },
                     {
                         "name": "服饰23",
                         "id": '2-3',
                         "percent": "70"
                     }
                 ]
             },
             {
                 "name": "图书",
                 "id": '3',
                 "percent": "50"
             }
         ]
     }


    function initResponse(target) {
        target.forEach(item => { // 用map返回无法对传参的原本对象进行修改 只能用引用的方式修改才会起作用 也就是object.attribute = ... 修改
            close[item['id']] = true // 对close进行初始化 根据每个id来进行赋值
            let childNode = item ?.children
            if (!!childNode) {
                initResponse(childNode)
            }
        })
    }

    function refresh() {
        let height = chartData.length * 35 + 120
        myChart.resize({
            height: height
        }) // 计算一个高度固定
        myChart.setOption(option)
    
    }

 
    function findLength(target) {
        let count = 0
        target.forEach(item => {
            count++
            let childNode = item ?.children
            if (!!childNode && !close[item.id]) {
                close[item.id] = !close[item.id]
                count += findLength(childNode)
            }
        })
        return count
    }


    let close = {}
    let data = response.data
    initResponse(data)
    let color = response.color
    let chartData = []

    data.forEach(item => {
        chartData.push({
            name: item['name'],
            value: item['percent'], // 把每一个bar都插入子元素
            itemStyle: {
                // color: color[item['id'].split('-').length - 1]
                normal: {
                    color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{
                        offset: 0,
                        // color: '#45bdc5'
                        color: '#ffffff'
                    },
                    {
                        offset: 1,
                        color: color[item['id'].split('-').length - 1]
                    }]),
                }
            },
            children: item['children'],
            id: item['id']
        })
    })
    let label = Object.assign([], chartData.map(item => {
        return {
            // value: '▶' + item['name'],
            value: (function () {
                if (!!item['children']) {
                    return '▶' + item['name'];
                } else {
                    return item['name'];
                }
            })(),
            textStyle: {
                color: color[item['id'].split('-').length - 1]
            },
            id: item['id']
        }
    }))

    const myChart = echarts.init(document.getElementById('main'));
    const option = {

        animationEasing: 'elasticOut',
        xAxis: {
            type: 'value'
        },
        yAxis: {
            type: 'category',
            data: label,
            animation: false,
            triggerEvent: true,
            axisLine: false,
            axisTick: false
        },
        series: [{
                data: chartData,
                type: 'bar',

                itemStyle: {
                    normal: {
                      color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{
                        offset: 0,
                        // color: '#45bdc5'
                        color: '#ffffff'
                      },
                      {
                        offset: 1,
                        color: 'rgba(113,237,255,0.1)'
                      }]),
                    }
                },
                label: {
                    show: true, //开启显示
                    position: 'right', //在上方显示
                    textStyle: { //数值样式
                        color: '#808080',
                        fontSize: '12'
                    }
                }


            }


        ]
    };

    myChart.on('click', function (params) {

        if (params.componentType == "xAxis") {
            // alert("单击了" + params.value + "x轴标签");
        } else if (params.componentType == "yAxis") {

            labelname = (params.value[0] == '▶' || params.value[0] == '▼') ? params.value.slice(1) : params
                .value

            labelindex = chartData.findIndex(item => {

                return ((item['name'][0] == '▶' || item['name'][0] == '▼') ? item['name'].slice(1) :
                    item.name) === labelname
            })
            // console.log(labelindex)
            let index = labelindex
            // 将开合状态交给close全局对象进行判断
            if (close[chartData[index].id]) {
                let childNode = chartData[index]?.children
                if (!!childNode) {
                    let childData = []
                    // 根据点击节点的children来进行添加结点
                    childNode.forEach(item => {
                        childData.push({
                            // name: '▶' +item['name'],
                            name: (function () {
                                if (!!item['children']) {
                                    return '▶' + item['name'];
                                } else {
                                    return item['name'];
                                }
                            })(),
                            value: item['percent'],
                            itemStyle: {
                                // color: color[item['id'].split('-').length - 1]
                                normal: {
                                    color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{
                                        offset: 0,
                                        // color: '#45bdc5'
                        color: '#ffffff'
                                    },
                                    {
                                        offset: 1,
                                        color: color[item['id'].split('-').length - 1]
                                    }]),
                                }
                            },
                            children: item['children'],
                            id: item['id']
                        })
                    })
                    close[chartData[index].id] = false // 修改开合状态

                    chartData.splice(index, 0, ...childData)
                    let cp = Object.assign({}, label[index])
                    label.splice(index, 1, {
                        ...cp,
                        value: '▼' + cp['value'].slice(1)
                    })
                    // 直接对 option.series[0].data 的数据进行更新也是可以的 更加方便
                    // 直接对 option进行更新就可以不用变量chartData.splice触发watch来进一步触发option更新
                    label.splice(index, 0, ...childData.map(item => {
                        return {
                            value: item['name'],
                            textStyle: {
                                color: color[item['id'].split('-').length - 1]
                            }
                        }
                    }))

                }
            } else {
                let childrenLength = findLength(chartData[index].children)
                close[chartData[index].id] = true
                let cp = Object.assign({}, label[index])
                label.splice(index, 1, {
                    ...cp,
                    value: '▶' + cp['value'].slice(1)
                })
                chartData.splice(index - childrenLength, childrenLength)
                label.splice(index - childrenLength, childrenLength)
            }
            refresh()
        } else {
            // alert("单击了"+params.name+"柱状图"+params.value);  
            let index = params.dataIndex
            // console.log(index)

            // 将开合状态交给close全局对象进行判断
            if (close[chartData[index].id]) {
                let childNode = params.data ?.children
                if (!!childNode) {
                    let childData = []
                    // 根据点击节点的children来进行添加结点
                    childNode.forEach(item => {
                        childData.push({
                            name: (function () {
                                if (!!item['children']) {
                                    return '▶' + item['name'];
                                } else {
                                    return item['name'];
                                }
                            })(),
                            value: item['percent'],
                            itemStyle: {
                                // color: color[item['id'].split('-').length - 1]
                                normal: {
                                    color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{
                                        offset: 0,
                                        // color: '#45bdc5'
                        color: '#ffffff'
                                    },
                                    {
                                        offset: 1,
                                        color: color[item['id'].split('-').length - 1]
                                    }]),
                                }
                            },
                            children: item['children'],
                            id: item['id']
                        })
                    })
                    close[chartData[index].id] = false // 修改开合状态

                    chartData.splice(index, 0, ...childData)
                    let cp = Object.assign({}, label[index])
                    label.splice(index, 1, {
                        ...cp,
                        value: '▼' + cp['value'].slice(1)
                    })

                    label.splice(index, 0, ...childData.map(item => {
                        return {
                            value: item['name'],
                            textStyle: {
                                color: color[item['id'].split('-').length - 1]
                            }
                        }
                    }))

                }
            } else {
                let childrenLength = findLength(params.data.children)
                close[chartData[index].id] = true
                let cp = Object.assign({}, label[index])
                label.splice(index, 1, {
                    ...cp,
                    value: '▶' + cp['value'].slice(1)
                })
                chartData.splice(index - childrenLength, childrenLength)
                label.splice(index - childrenLength, childrenLength)
            }
            refresh()

        }
    })
    refresh()
</script>

</html>

  • 效果图:
    在这里插入图片描述
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值