index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. <template>
  2. <div id="main" class="echarts-container" />
  3. </template>
  4. <script>
  5. import { TreeChart } from "echarts/charts";
  6. import { TooltipComponent } from "echarts/components";
  7. import * as echarts from "echarts/core";
  8. import { CanvasRenderer } from "echarts/renderers";
  9. echarts.use([TooltipComponent, TreeChart, CanvasRenderer]);
  10. // let this.myChart;
  11. let option;
  12. var treeNodePadding = 50; //节点间最小间隔
  13. var treeTopPadding = 120; //tree距顶端的距离
  14. var rightNode; //最右侧节点,用于计算偏移量
  15. export default {
  16. name: "TreeChart",
  17. data() {
  18. return {
  19. myChart: null,
  20. centerLoca: [],
  21. fontSize: 6,
  22. chartWidth: 0,
  23. chartHeight: 0,
  24. center: [this.chartWidth, "50%"],
  25. };
  26. },
  27. props: {
  28. treeData: {
  29. type: Object,
  30. default: () => {
  31. return {};
  32. },
  33. },
  34. topDeep: Number,
  35. type: String,
  36. clickNodeList: Array,
  37. },
  38. watch: {
  39. clickNodeList: {
  40. handler(newValue, old) {
  41. if (newValue) {
  42. this.init();
  43. }
  44. },
  45. },
  46. treeData: {
  47. handler(newValue, old) {
  48. console.log(newValue);
  49. if (newValue) {
  50. this.init();
  51. }
  52. },
  53. },
  54. chartWidth: {
  55. handler(newValue, old) {
  56. console.log(newValue);
  57. if (newValue) {
  58. this.init();
  59. }
  60. },
  61. },
  62. },
  63. mounted() {
  64. this.init();
  65. // 监听树图节点的点击事件
  66. this.myChart.on("click", (e) => {
  67. console.log("e:", e);
  68. this.$emit("clickNode", e);
  69. });
  70. },
  71. methods: {
  72. init() {
  73. const that = this;
  74. // console.log('$el:', this.$el)
  75. that.myChart = echarts.init(this.$el);
  76. that.myChart.clear();
  77. option = {
  78. tooltip: {
  79. // 提示框浮层设置
  80. trigger: "item",
  81. triggerOn: "mousemove", // 提示框触发条件
  82. enterable: true, // 鼠标是否可进入提示框浮层中,默认false
  83. confine: true, // 是否将tooltip框限制在图表的区域内
  84. padding: [5, 10],
  85. formatter: function (params) {
  86. // 提示框浮层内容格式器,支持字符串模板和回调函数两种形式
  87. const relationStr =
  88. "<div style='display: flex;flex-direction: column;'>" +
  89. "<div>" +
  90. '<span style="color: #606266;">' +
  91. that.$t("atlas.numberLayers") +
  92. ": " +
  93. "</span>" +
  94. '<span style="color: #000000;">' +
  95. (Number(params.data.TOP_NETWORK_DEEP) - Number(that.topDeep)) +
  96. "</span>" +
  97. "</div>" +
  98. "<div>" +
  99. '<span style="color: #606266;">' +
  100. that.$t("atlas.memberCode") +
  101. ": " +
  102. "</span>" +
  103. '<span style="color: #000000;">' +
  104. params.data.USER_NAME +
  105. "</span>" +
  106. "</div>" +
  107. "<div>" +
  108. '<span style="color: #606266;">' +
  109. that.$t("atlas.name") +
  110. ": " +
  111. "</span>" +
  112. '<span style="color: #000000;">' +
  113. params.data.REAL_NAME +
  114. "</span>" +
  115. "</div>" +
  116. "<div>" +
  117. '<span style="color: #606266;">' +
  118. that.$t("atlas.level") +
  119. ": " +
  120. "</span>" +
  121. '<span style="color: #000000;">' +
  122. params.data.DEC_LV_NAME +
  123. "</span>" +
  124. "</div>" +
  125. "<div>" +
  126. '<span style="color: #606266;">' +
  127. that.$t("atlas.highest") +
  128. ": " +
  129. "</span>" +
  130. '<span style="color: #000000;">' +
  131. params.data.EMP_LV_NAME +
  132. "," +
  133. params.data.CROWN_LV_NAME +
  134. "</span>" +
  135. "</div>" +
  136. "<div>" +
  137. '<span style="color: #606266;">' +
  138. that.$t("atlas.periodNumber") +
  139. ": " +
  140. "</span>" +
  141. '<span style="color: #000000;">' +
  142. params.data.PERIOD_AT +
  143. "</span>" +
  144. "</div>" +
  145. "</div>";
  146. const networkStr =
  147. "<div style='display: flex;flex-direction: column;'>" +
  148. "<div>" +
  149. '<span style="color: #606266;">' +
  150. that.$t("atlas.memberCode") +
  151. ": " +
  152. "</span>" +
  153. '<span style="color: #000000;">' +
  154. params.data.USER_NAME +
  155. "</span>" +
  156. "</div>" +
  157. "<div>" +
  158. '<span style="color: #606266;">' +
  159. that.$t("atlas.name") +
  160. ": " +
  161. "</span>" +
  162. '<span style="color: #000000;">' +
  163. params.data.REAL_NAME +
  164. "</span>" +
  165. "</div>" +
  166. "<div>" +
  167. '<span style="color: #606266;">' +
  168. that.$t("atlas.level") +
  169. ": " +
  170. "</span>" +
  171. '<span style="color: #000000;">' +
  172. params.data.DEC_LV_NAME +
  173. "</span>" +
  174. "</div>" +
  175. "<div>" +
  176. '<span style="color: #606266;">' +
  177. that.$t("atlas.highest") +
  178. ": " +
  179. "</span>" +
  180. '<span style="color: #000000;">' +
  181. params.data.EMP_LV_NAME +
  182. "," +
  183. params.data.CROWN_LV_NAME +
  184. "</span>" +
  185. "</div>" +
  186. "<div>" +
  187. '<span style="color: #606266;">' +
  188. that.$t("atlas.periodNumber") +
  189. ": " +
  190. "</span>" +
  191. '<span style="color: #000000;">' +
  192. params.data.PERIOD_AT +
  193. "</span>" +
  194. "</div>" +
  195. "</div>";
  196. return that.type === "placement" ? relationStr : networkStr;
  197. },
  198. // valueFormatter: function (value) { // tooltip 中数值显示部分的格式化回调函数
  199. // return '$' + value.toFixed(2)
  200. // },
  201. backgroundColor: "rgba(250,250,250,0.99)", // 提示框浮层的背景颜色
  202. borderColor: "#1890FF", // 提示框浮层的边框颜色
  203. borderWidth: 0.5, // 提示框浮层的边框宽
  204. borderRadius: 8, // 提示框浮层圆角
  205. textStyle: {
  206. // 提示框浮层的文本样式
  207. color: "#333", // 文字颜色
  208. // fontWeight: 400, // 字体粗细
  209. // fontSize: that.fontSize, // 字体大小
  210. // lineHeight: 20, // 行高
  211. // width: 60, // 文本显示宽度
  212. // 文字超出宽度是否截断或者换行;只有配置width时有效
  213. overflow: "breakAll", // truncate截断,并在末尾显示ellipsis配置的文本,默认为...;break换行;breakAll换行,并强制单词内换行
  214. ellipsis: "...",
  215. },
  216. extraCssText:
  217. "min-height:100px;box-shadow: 0 0 9px rgba(0, 0, 0, 0.3);text-align: left;", // 额外添加到浮层的css样式
  218. axisPointer: {
  219. type: "shadow",
  220. shadowStyle: {
  221. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  222. { offset: 0, color: "rgba(255, 255, 255, 0)" },
  223. { offset: 1, color: "rgba(37, 107, 230, 0.18)" },
  224. ]),
  225. },
  226. },
  227. },
  228. series: [
  229. {
  230. width: that.chartWidth + "px",
  231. height: "100%",
  232. type: "tree",
  233. data: [this.treeData],
  234. name: "树图",
  235. top: "1%", // 组件离容器上侧的距离,像素值20,或相对容器的百分比
  236. left: "1%", // 组件离容器左侧的距离
  237. bottom: "1%", // 组件离容器下侧的距离
  238. right: "1%", // 组件离容器右侧的距离
  239. layout: "orthogonal", // 树图的布局,正交orthogonal和径向radial两种
  240. orient: "TB", // 树图中正交布局的方向,'LR','RL','TB','BT',只有布局是正交时才生效
  241. edgeShape: "polyline", // 树图边的形状,有曲线curve和折线polyline两种,只有正交布局下生效
  242. // edgeForkPosition:"10%",
  243. roam: true, // 是否开启鼠标缩放或平移,默认false
  244. zoom: 0.5,
  245. scaleLimit: {
  246. min: 0.5,
  247. max: 10,
  248. },
  249. center: that.center || null,
  250. // center: [5, 5],
  251. initialTreeDepth: -1, // 树图初始的展开层级(深度),根节点是0,不设置时全部展开
  252. // symbol: 'emptyCircle', // 标记的图形,默认是emptyCircle;circle,rect,roundRect,triangle,diamond,pin,arrow,none
  253. symbol: function (value, params) {
  254. // params.data节点的所有数据
  255. if (params.data.leaf == true) {
  256. return "emptyCircle";
  257. } else if (params.data.leaf == false) {
  258. return "circle";
  259. }
  260. },
  261. // symbolRotate: 270, // 配合arrow图形使用效果较好
  262. symbolSize: 10, // 大于0时是圆圈,等于0时不展示,标记的大小
  263. itemStyle: {
  264. // 树图中每个节点的样式
  265. color: "#1890FF", // 节点未展开时的填充色
  266. borderColor: "rgba(255, 144, 0, 1)", // 图形的描边颜色
  267. borderWidth: 0.5, // 描边线宽,为0时无描边
  268. borderType: "dotted", // 描边类型
  269. borderCap: "square", // 指定线段末端的绘制方式butt方形结束,round圆形结束,square
  270. shadowColor: "rgba(0,121,221,0.3)", // 阴影颜色
  271. shadowBlur: 12, // 图形阴影的模糊大小
  272. opacity: 1, // 图形透明度
  273. },
  274. label: {
  275. // 每个节点对应的文本标签样式
  276. show: true, // 是否显示标签
  277. // distance: 22, // 文本距离图形元素的距离
  278. position: "bottom", // 标签位置
  279. verticalAlign: "middle", // 文字垂直对齐方式,默认自动,top,middle,bottom
  280. align: "center", // 文字水平对齐方式,默认自动,left,right,center
  281. fontSize: that.fontSize, // 字体大小
  282. color: "#333", // 字体颜色
  283. backgroundColor: "#F0F5FA", // 文字块的背景颜色
  284. borderColor: "#1890FF", // 文字块边框颜色
  285. borderWidth: 0.5, // 文字块边框宽度
  286. borderType: "solid", // 文字块边框描边类型 solid dashed dotted
  287. borderRadius: 6, // 文字块的圆角
  288. padding: [5, 6], // 文字块内边距
  289. shadowColor: "rgba(0,121,221,0.6)", // 文字块的背景阴影颜色
  290. shadowBlur: 6, // 文字块的背景阴影长度
  291. width: 80,
  292. // 文字超出宽度是否截断或者换行;只有配置width时有效
  293. overflow: "truncate", // truncate截断,并在末尾显示ellipsis配置的文本,默认为...;break换行;breakAll换行,并强制单词内换行
  294. ellipsis: "...",
  295. formatter: function (params) {
  296. return (
  297. params.data.USER_NAME + "\n" + "\n" + params.data.REAL_NAME
  298. );
  299. // return params.data.REAL_NAME
  300. },
  301. },
  302. lineStyle: {
  303. // 树图边的样式
  304. color: "rgba(0,0,0,.35)", // 树图边的颜色
  305. width: 1, // 树图边的宽度
  306. curveness: 0.5, // 树图边的曲度
  307. shadowColor: "rgba(0, 0, 0, 0.5)", // 阴影颜色
  308. shadowBlur: 10, // 图形阴影的模糊大小
  309. },
  310. emphasis: {
  311. // 树图中图形和标签高亮的样式
  312. disabled: false, // 是否关闭高亮状态,默认false
  313. // 在高亮图形时,是否淡出其它数据的图形已达到聚焦的效果
  314. focus: "relative", // none不淡出其他图形(默认);self只聚焦当前高亮的数据图形;series聚焦当前高亮的数据所在系列的所有图形;ancestor聚焦所有祖先节点;descendant聚焦所有子孙节点;relative聚焦所有子孙和祖先节点
  315. blurScope: "coordinateSystem", // 开启focus时,配置淡出的范围,coordinateSystem淡出范围为坐标系(默认);series淡出范围为系列;global淡出范围为全局
  316. itemStyle: {
  317. // 该节点的样式
  318. color: "#1890FF", // 图形的颜色
  319. // borderColor: 'rgba(255, 144, 0, 1)', // 图形的描边颜色
  320. // borderWidth: 1, // 描边线宽,为0时无描边
  321. borderType: "solid", // 描边类型 solid dashed dotted
  322. borderCap: "square", // 指定线段末端的绘制方式butt方形结束,round圆形结束,square
  323. shadowColor: "rgba(0,121,221,0.3)", // 阴影颜色
  324. shadowBlur: 12, // 图形阴影的模糊大小
  325. opacity: 1, // 图形透明度
  326. },
  327. lineStyle: {
  328. // 树图边的样式
  329. color: "rgba(0,0,0,.45)", // 树图边的颜色
  330. width: 2, // 树图边的宽度
  331. curveness: 0.5, // 树图边的曲度
  332. shadowColor: "rgba(0, 0, 0, 0.5)", // 阴影颜色
  333. shadowBlur: 6, // 图形阴影的模糊大小
  334. },
  335. label: {
  336. // 高亮标签的文本样式
  337. color: "#333",
  338. fontWeight: 600,
  339. },
  340. },
  341. blur: {
  342. // 淡出状态的相关配置,开启emphasis.focus后有效
  343. itemStyle: {}, // 节点的样式
  344. lineStyle: {}, // 树图边的样式
  345. label: {}, // 淡出标签的文本样式
  346. },
  347. leaves: {
  348. // 叶子节点的特殊配置
  349. label: {
  350. // 叶子节点的文本标签样式
  351. // distance: 22,
  352. // color: '#1890FF',
  353. position: "bottom",
  354. verticalAlign: "middle",
  355. align: "center",
  356. },
  357. itemStyle: {}, // 叶子节点的样式
  358. emphasis: {}, // 叶子节点高亮状态的配置
  359. blur: {}, // 叶子节点淡出状态的配置
  360. select: {}, // 叶子节点选中状态的配置
  361. },
  362. animation: true, // 是否开启动画
  363. expandAndCollapse: true, // 子树折叠和展开的交互,默认打开
  364. animationDuration: 500, // 初始动画的时长
  365. animationEasing: "linear", // 初始动画的缓动效果
  366. animationDelay: 0, // 初始动画的延迟
  367. animationDurationUpdate: 500, // 数据更新动画的时长
  368. animationEasingUpdate: "cubicInOut", // 数据更新动画的缓动效果
  369. animationDelayUpdate: 0, // 数据更新动画的延迟
  370. },
  371. ],
  372. };
  373. option && that.myChart.setOption(option, true);
  374. that.resize();
  375. // that.adjustTreeView();
  376. that.myChart.getZr().off("mousewheel");
  377. that.myChart.getZr().on("mousewheel", (param) => {
  378. let currentOption = that.myChart.getOption();
  379. // console.log(currentOption);
  380. if (currentOption.series[0]) {
  381. let zoom = currentOption.series[0].zoom;
  382. // that.fontSize = 10 * zoom
  383. currentOption.textStyle.fontSize = 10 * zoom;
  384. currentOption.series[0].label.fontSize = 8 * zoom;
  385. currentOption.series[0].label.width = 110 * zoom;
  386. currentOption.series[0].label.distance = 18 * zoom;
  387. currentOption.series[0].leaves.distance = 18 * zoom;
  388. option && that.myChart.setOption(currentOption);
  389. }
  390. });
  391. },
  392. resize() {
  393. let elesArr = Array.from(
  394. new Set(this.myChart._chartsViews[0]._data._graphicEls)
  395. );
  396. console.log(elesArr.length)
  397. let dep = this.myChart._chartsViews[0]._data.tree.root.height; //获取树高
  398. console.log(dep);
  399. let layer_height = 90; //层级之间的高度
  400. let currentHeight = layer_height * (dep + 1) || layer_height;
  401. let newHeight = Math.max(currentHeight, layer_height);
  402. this.chartHeight = newHeight + "px";
  403. let layer_width = 90; // 兄弟节点之间的距离
  404. let currentWidth = layer_width * (elesArr.length - 1) || layer_width;
  405. let newWidth = Math.max(currentWidth, layer_width);
  406. console.log(newWidth);
  407. if (newWidth < 200) {
  408. this.center[0] = '-70%';
  409. this.center[1] = (dep * 2) + '0%';
  410. }else if (newWidth > 200 && newWidth < 500) {
  411. this.center[0] = '-56%';
  412. this.center[1] = (dep * 1.5) + '0%';
  413. }else if (newWidth > 500 && newWidth < 1000) {
  414. this.center[0] = '-30%';
  415. this.center[1] = (dep) + '0%';
  416. } else if (newWidth > 1000){
  417. this.center[0] = elesArr.length > 60 ? newWidth * 1.5 : newWidth * 1.2;
  418. this.center[1] = (dep ) + '0%';
  419. }
  420. this.chartWidth = newWidth;
  421. window.addEventListener("resize", () => {
  422. this.myChart.resize();
  423. });
  424. },
  425. doGlobalTreeChart(ec, data) {
  426. // this.myChart.setOption(getGlobalTreeOption());
  427. //生成图表后做调整
  428. this.adjustTreeView();
  429. },
  430. //调整tree显示
  431. adjustTreeView() {
  432. var zr = this.myChart.getZr();
  433. var domWidth = zr.painter.getWidth();
  434. var treeWidth = this.getTreeWidth(zr);
  435. if (treeWidth <= domWidth) return;
  436. },
  437. //计算最左边节点和最右边节点(symbol为image或icon)的间隔即为树图宽度
  438. getTreeWidth(zr) {
  439. var nodes = zr.storage._roots;
  440. console.log(nodes);
  441. let max = 0;
  442. let min = 0;
  443. for (var i = 0; i < nodes.length; i++) {
  444. if (nodes[i].type == "image" || nodes[i].type == "icon") {
  445. var nodeX = nodes[i].style.x;
  446. if (nodeX > max) {
  447. max = nodeX;
  448. rightNode = nodes[i];
  449. continue;
  450. }
  451. if (nodeX < min) {
  452. min = nodeX;
  453. }
  454. }
  455. }
  456. return max - min;
  457. },
  458. },
  459. };
  460. </script>
  461. <style lang="scss" scoped>
  462. .echarts-container {
  463. width: 100%;
  464. height: calc(100vh - 105px);
  465. }
  466. </style>