昨天,写了一篇关于圆环进度条的博客(请移步:Vue/React圆环进度条),已经烦不胜烦,今天又遇到了需要展示类似公司的组织结构图的功能需求,要冒了!!!
这种需求,自己用div+css也是可以实现的,但是没有什么动画效果,我的css3又很差劲,而且项目中已经使用到了折线图、饼状图、柱状图之类的图表,用的还是百度的echarts,所以这个组织结构图之类的需求也就用了百度的echarts来实现了。
说实话,以前用echarts写折线图、柱状图、饼状图的较多,它的API还算比较熟悉,但是画组织结构这样的树状图就很苦逼了,没用过啊,而且设计给的树状图的展示效果跟echarts树状图的展示效果相去甚远,我滴孩,又得一通费时费力的研究,设计图如下:
如图所示,一个树节点中可能会有两种不同的背景色,还有两种不同的文字颜色,每个节点展示的还是圆角矩形。有同学说了,echarts有设置圆角的API啊,直接设置不就完事了。我想说的是,它是提供的有这样的API,但是按照正常的套路实现不了啊。
从图上还可以看到一个几乎实现不了的效果,就是连接每个节点之间的线的拐角处都是直角而不是平滑的,而且echarts没有给出可以设置拐角处是直角的API,只是给了一个curveness(API的描述是树图边的曲度),这玩意儿使用了之后,也还是实现不了的。
从网上查了资料,有人说可以修改echarts的源码,这种解决办法我不推荐,是因为在vue或react项目中,echarts是需要通过安装在package.json中的,如果是多人并行开发,那么别人安装的echarts就不是你修改后的echarts,这就是问题所在。
说了那么多,还是上代码吧,该代码是基于vue的,如果要使用在react中,稍微修改一下就可以了。
组件tree.vue:
<template> <div :class="className" :style="{height:height,width:width}" /> </template> <script> import echarts from "echarts"; require("echarts/theme/macarons"); import { debounce } from "@/utils"; export default { props: { className: { type: String, default: "chart" }, width: { type: String, default: "100%" }, height: { type: String, default: "500px" }, chartData: { type: Object, required: true } }, data() { return { chart: null, }; }, watch: { chartData: { deep: true, handler(val) { this.setOptions(val); } } }, mounted() { this.initChart(); //是否需要自适应-加了防抖函数 this.__resizeHandler = debounce(() => { if (this.chart) { this.chart.resize(); } }, 100); window.addEventListener("resize", this.__resizeHandler); // 监听侧边栏的变化以实现自适应缩放 const sidebarElm = document.getElementsByClassName("sidebar-container")[0]; sidebarElm.addEventListener("transitionend", this.sidebarResizeHandler); }, beforeDestroy() { if (!this.chart) { return; } window.removeEventListener("resize", this.__resizeHandler); this.chart.dispose(); this.chart = null; const sidebarElm = document.getElementsByClassName("sidebar-container")[0]; sidebarElm.removeEventListener("transitionend", this.sidebarResizeHandler); }, methods: { initChart() { this.chart = echarts.init(this.$el, "macarons"); this.setOptions(this.chartData); }, setOptions(data) { this.chart.setOption({ //提供数据视图、还原、下载的工具 // toolbox: { // show : true, // feature : { // mark : {show: true}, // dataView : {show: true, readOnly: false}, // restore : {show: true}, // saveAsImage : {show: true} // } // }, series: [ { name: "统一授信视图", type: "tree", orient: "TB", //竖向或水平 TB代表竖向 LR代表水平 top: '10%', initialTreeDepth: 10, //树图初始展开的层级(深度) expandAndCollapse: false, //点击节点时不收起子节点,default: true symbolSize: [135, 65], itemStyle: { color: 'transparent', borderWidth: 0, }, lineStyle: { color: '#D5D5D5', width: 1, curveness: 1, }, data: [data] } ] }); }, sidebarResizeHandler(e) { if (e.propertyName === "width") { this.__resizeHandler(); } } } }; </script>使用tree.vue的方法:
<template> <tree :chartData="treeData" /> </template> <script> import tree from './tree'; export default { data() { return { treeData: { label: { backgroundColor: '#F4F4F4', borderRadius: [0, 0, 5, 5], formatter: [ '{first|综合授信额度}', '{second|(CR20190912000013)\n获批金额:100\n币种:人民币}', ].join('\n'), rich: { first: { backgroundColor: '#078E34', color: '#fff', align: 'center', width: 135, height: 30, borderRadius: [5, 5, 0, 0], }, second: { color: '#888', align: 'center', lineHeight: 17, }, } }, children: [ { label: { formatter: [ '{first|渠道额度}', ].join('\n'), rich: { first: { backgroundColor: '#3AC082', color: '#fff', align: 'center', width: 135, height: 65, borderRadius: 5, }, } }, children: [{ label: { formatter: [ '{first|保理额度}', ].join('\n'), rich: { first: { backgroundColor: '#3AC082', color: '#fff', align: 'center', width: 135, height: 65, borderRadius: 5, }, } }, children: [{ label: { backgroundColor: '#F4F4F4', borderRadius: [0, 0, 5, 5], formatter: [ '{first|反向保理}', '{second|(CR20190912000013)\n获批金额:100\n币种:人民币}', ].join('\n'), rich: { first: { backgroundColor: '#078E34', color: '#fff', align: 'center', width: 135, height: 30, borderRadius: [5, 5, 0, 0], }, second: { color: '#888', align: 'center', lineHeight: 17, }, } }, }] }] }, { label: { formatter: [ '{first|担保/(乐)集团/其他额度}', ].join('\n'), rich: { first: { backgroundColor: '#3AC082', color: '#fff', align: 'center', width: 135, height: 65, borderRadius: 5, }, } }, children: [{ label: { formatter: [ '{first|保理额度}', ].join('\n'), rich: { first: { backgroundColor: '#3AC082', color: '#fff', align: 'center', width: 135, height: 65, borderRadius: 5, }, } }, children: [{ label: { backgroundColor: '#F4F4F4', borderRadius: [0, 0, 5, 5], formatter: [ '{first|正向保理}', '{second|(CR20190912000013)\n获批金额:100\n币种:人民币}', ].join('\n'), rich: { first: { backgroundColor: '#B8D87E', color: '#fff', align: 'center', width: 135, height: 30, borderRadius: [5, 5, 0, 0], }, second: { color: '#888', align: 'center', lineHeight: 17, }, } }, }] }, { label: { formatter: [ '{first|租赁额度}', ].join('\n'), rich: { first: { backgroundColor: '#3AC082', color: '#fff', align: 'center', width: 135, height: 65, borderRadius: 5, }, } }, children: [ { label: { backgroundColor: '#F4F4F4', borderRadius: [0, 0, 5, 5], formatter: [ '{first|车辆租赁}', '{second|(CR20190912000013)\n获批金额:100\n币种:人民币}', ].join('\n'), rich: { first: { backgroundColor: '#FF6C6A', color: '#fff', align: 'center', width: 135, height: 30, borderRadius: [5, 5, 0, 0], }, second: { color: '#888', align: 'center', lineHeight: 17, }, } }, }, ] }] } ] } } }, components: { tree, } }; </script>