js写一个这样的组织架构图,下面是代码(复制两份代码,安装依赖,可以看到效果,依赖:vue.js element-ui)
代码地址:https://gitee.com/xxsheep/organization_chart
1.插件官网https://balkangraph.com/ (官网上说有收费版,但是下载的是免费版的)
可以下载js文件,也可以用npm安装
cnpm i @balkangraph/orgchart.js -S
2.修改成自己想要的样式(因为没太多时间,只简单的改成了自己要做的样子,也没有好好处理代码,该文件可直接复制使用,setTemp是修改模板),下面是代码
// 组织机构图
import OrgChart from '@balkangraph/orgchart.js/orgchart';
export default class Chart {
direction = 'top' // 图形方向: top-从上到下,left-从左到右
idKey = 'orgId' // 节点id key
parentIdKey = 'orgParentId' // 节点的父id key
childKey = 'childList' // 子节点key
// 初始化
init(dom, treeData) {
let tree = this.delParentId(treeData);
let list = [];
this.treeArray(tree, list);
return this.draw(dom, list);
}
// 删除顶级节点的父id
delParentId(treeData) {
if (!treeData) {
console.error('机构图数据为空,请检查')
return false;
}
if (!Array.isArray(treeData)) {
console.error('机构图数据必须是数组,请检查')
return false;
}
if (treeData.length === 0) {
return false;
}
let list = treeData.map(item => {
delete item[this.parentIdKey];
return item;
})
return list;
}
// 把树形格式化为数组
treeArray(list, result = []) {
if (list && Array.isArray(list) && list.length > 0) {
list.forEach(item => {
item.id = item[this.idKey];
item.pid = item[this.parentIdKey];
item.name = `${item.orgManagerName}/${item.orgManagerPositionName}`;
item.number = `${item.staffNumbers}/${item.planNumbers}`;
item.img = require('../img/user/user.png');
result.push(item);
this.treeArray(item[this.childKey], result);
});
}
}
// 渲染机构树图形
draw(dom, list){
this.setTmp();
let nodes = list.map(item => {
item.tags = ["Management"];
return item;
})
return new OrgChart(dom, {
nodes: nodes,
nodeBinding: {
field_0: "orgName",
field_1: "name",
field_2: "number",
img_0: "img"
},
tags: {
Management: {
template: "myTemplate"
}
},
// menu: {
// pdf: { text: "导出 PDF" },
// png: { text: "导出 PNG" },
// svg: { text: "导出 SVG" },
// csv: { text: "导出 CSV" }
// },
toolbar: {
layout: false,
zoom: true,
fit: true,
expandAll: false
},
orientation: OrgChart.orientation[this.direction], // 方向
enableSearch: false, // 是否可以搜索
nodeMouseClick: OrgChart.action.none, // 关闭点击后出现的效果,该效果有很多,可以去官网查看
});
}
// 模板
setTmp() {
OrgChart.templates.myTemplate = Object.assign({}, OrgChart.templates.ana);
OrgChart.templates.myTemplate.size = [168, 80];
// OrgChart.templates.myTemplate.node = '<circle cx="100" cy="100" r="100" fill="#fff" stroke-width="1" stroke="#1C1C1C"></circle>';
// OrgChart.templates.myTemplate.node = '<rect class="hy_node" width="168" height="80" rx="4" ry="4" fill="#fff" ></rect>';
OrgChart.templates.myTemplate.node =
`<g>
<rect class="hy_node" width="168" height="80" x="0" y="0" rx="4" ry="4" fill="#ccc" filter="url(#f1)" ></rect>
<filter id="f1" x="0" y="0">
<feGaussianBlur in="SourceGraphic" stdDeviation="4" />
</filter>
<rect class="hy_node" width="168" height="80" rx="4" ry="4" fill="#fff" ></rect>
</g>
`;
// 鼠标点击效果
OrgChart.templates.myTemplate.ripple = {
radius: 0,
color: "#FC8E58",
rect: null
};
OrgChart.templates.myTemplate.field_0 =
`<g>
<rect class="hy_node" width="168" height="32" rx="4" ry="4" fill="#FC8E58" ></rect>
<rect class="hy_node" width="168" y="12" height="20" fill="#FC8E58" ></rect>
<text style="font-size: 14px;" fill="#fff" x="84" y="21" text-anchor="middle">{val}</text>
</g>
`;
OrgChart.templates.myTemplate.field_1 = '<text style="font-size: 12px;" width="102" text-overflow="ellipsis" fill="#676B6D" x="52" y="52" text-anchor="left">{val}</text>';
OrgChart.templates.myTemplate.field_2 = '<text style="font-size: 12px;" width="102" text-overflow="ellipsis" fill="#FF8C58" x="52" y="70" text-anchor="left">{val}</text>';
OrgChart.templates.myTemplate.img_0 =
'<clipPath id="ulaImg">'
+ '<rect class="hy_node_img" x="8" y="40" width="32" height="32" fill="#ccc" ></rect>'
+ '</clipPath>'
+ '<image preserveAspectRatio="xMidYMid slice" clip-path="url(#ulaImg)" xlink:href="{val}" x="8" y="40" width="32" height="32">'
+ '</image>';
// 连接线条
// OrgChart.templates.myTemplate.link = '<path stroke="#686868" stroke-width="1px" fill="none" link-id="[{id}][{child-id}]" d="M{xa},{ya} C{xb},{yb} {xc},{yc} {xd},{yd}" />';
// OrgChart.templates.myTemplate.nodeMenuButton =
// '<g style="cursor:pointer;" transform="matrix(1,0,0,1,93,15)" control-node-menu-id="{id}">'
// + '<rect x="-4" y="-10" fill="#000000" fill-opacity="0" width="22" height="22">'
// + '</rect>'
// + '<line x1="0" y1="0" x2="0" y2="10" stroke-width="2" stroke="#0890D3" />'
// + '<line x1="7" y1="0" x2="7" y2="10" stroke-width="2" stroke="#0890D3" />'
// + '<line x1="14" y1="0" x2="14" y2="10" stroke-width="2" stroke="#0890D3" />'
// + '</g>';
OrgChart.templates.myTemplate.plus =
'<rect x="6" y="7" width="16" height="16" rx="8" ry="8" fill="#fff" stroke="#aeaeae" stroke-width="1"></rect>'
+ '<line x1="10" y1="15" x2="18" y2="15" stroke-width="1" stroke="#aeaeae"></line>'
+ '<line x1="14" y1="11" x2="14" y2="19" stroke-width="1" stroke="#aeaeae"></line>'
OrgChart.templates.myTemplate.minus =
'<rect x="6" y="7" width="16" height="16" rx="8" ry="8" fill="#fff" stroke="#aeaeae" stroke-width="1"></rect>'
+ '<line x1="10" y1="15" x2="18" y2="15" stroke-width="1" stroke="#aeaeae"></line>'
// OrgChart.templates.myTemplate.expandCollapseSize = 36;
// OrgChart.templates.myTemplate.exportMenuButton =
// '<div style="position:absolute;right:{p}px;top:{p}px; width:40px;height:50px;cursor:pointer;" control-export-menu="">'
// + '<hr style="background-color: #0890D3; height: 3px; border: none;">'
// + '<hr style="background-color: #0890D3; height: 3px; border: none;">'
// + '<hr style="background-color: #0890D3; height: 3px; border: none;">'
// + '</div>';
// OrgChart.templates.myTemplate.pointer =
// '<g data-pointer="pointer" transform="matrix(0,0,0,0,100,100)">><g transform="matrix(0.3,0,0,0.3,-17,-17)">'
// + '<polygon fill="#0890D3" points="53.004,173.004 53.004,66.996 0,120" />'
// + '<polygon fill="#0890D3" points="186.996,66.996 186.996,173.004 240,120" />'
// + '<polygon fill="#0890D3" points="66.996,53.004 173.004,53.004 120,0" />'
// + '<polygon fill="#0890D3" points="120,240 173.004,186.996 66.996,186.996" />'
// + '<circle fill="#0890D3" cx="120" cy="120" r="30" />'
// + '</g></g>';
}
constructor(dom, treeData ,direction = 'top') {
this.direction = direction;
return this.init(dom, treeData);
}
}
3.调用
<style lang="scss" scoped>
.test{
padding: 10px;
}
.operation{
padding: 10px 24px;
}
.chart {
margin: 0 24px 18px 24px;
border: 1px solid rgba(241, 242, 242, 1);
height: 800px;
margin-top: 20px;
}
</style>
<template>
<div class="test">
<el-row :gutter="16" class="operation">
<el-col :span=".5">
<el-select v-model="direction" size="small" placeholder="显示方向" @change="directionChange">
<el-option value="top" label="竖"></el-option>
<el-option value="left" label="横"></el-option>
</el-select>
</el-col>
<el-col :span=".5">
<el-select v-model="layer" size="small" placeholder="显示层级" @change="layerChange">
<el-option :value="0" label="全部显示"></el-option>
<el-option v-for="item in layerList" :key="item" :value="item" :label="`${item}层`"></el-option>
</el-select>
</el-col>
<el-col :span=".5">
<el-button type="primary" size="small" @click="downloadPic">导出</el-button>
</el-col>
</el-row>
<div ref="orgChartDom" class="chart"></div>
</div>
</template>
<script>
import OrgChart from '../../assets/js/orgChart';
export default {
name: "test",
data() {
return {
direction: 'top',
layer: 0,
layerList: [1,2,3,4,5,6,7,8,9],
treeData: [
{
"orgId":28,
"orgParentId":0,
"orgName":"水果集团",
"orgManagerName":"xun悟空",
"orgManagerPositionName":"集团老总",
"staffNumbers":200,
"planNumbers":240,
"childList":[
{
"orgId":671,
"orgParentId":28,
"orgName":"南方公司",
"orgManagerName":"ju八戒",
"orgManagerPositionName":"厂长",
"staffNumbers":100,
"planNumbers":120,
"childList":[
{
"orgId":1460,
"orgParentId":671,
"orgName":"种植部",
"orgManagerName":"牛郎",
"orgManagerPositionName":"部长",
"staffNumbers":100,
"planNumbers":120
},
{
"orgId":1464,
"orgParentId":671,
"orgName":"运输部",
"orgManagerName":"哮天犬",
"orgManagerPositionName":"部长",
"staffNumbers":100,
"planNumbers":120,
"childList":[
{
"orgId":1478,
"orgParentId":1464,
"orgName":"运输一队",
"orgManagerName":"大黄",
"orgManagerPositionName":"队长",
"staffNumbers":100,
"planNumbers":120
}
]
},
{
"orgId":1466,
"orgParentId":671,
"orgName":"加工部",
"orgManagerName":"织女",
"orgManagerPositionName":"部长",
"staffNumbers":100,
"planNumbers":120
},
{
"orgId":1488,
"orgParentId":671,
"orgName":"销售部",
"orgManagerName":"叨叨哥",
"orgManagerPositionName":"部长",
"staffNumbers":100,
"planNumbers":120
},
{
"orgId":1489,
"orgParentId":671,
"orgName":"财务部",
"orgManagerName":"武大郎",
"orgManagerPositionName":"部长",
"staffNumbers":100,
"planNumbers":120
}
]
},
{
"orgId":1490,
"orgParentId":28,
"orgName":"北方公司",
"orgManagerName":"沙僧",
"orgManagerPositionName":"厂长",
"staffNumbers":100,
"planNumbers":120
}
]
}
],
};
},
mounted() {
// this.draw(this.$refs.tree, this.nodes);
this.getChartData();
},
methods: {
// 机构图--获取机构图数据
getChartData() {
this.orgChartData = this.treeData;
this.showOrgChartData = this.deepClone(this.treeData);
this.orgChartObject = new OrgChart(this.$refs.orgChartDom, this.showOrgChartData, 'top');
},
// 机构图--显示方向改变
directionChange(v) {
if (this.showOrgChartData) {
this.orgChartObject = new OrgChart(this.$refs.orgChartDom, this.showOrgChartData, v);
}
},
// 层级改变
layerChange(v) {
let layer = v === 0 ? 999 : v;
this.showOrgChartData = this.deepClone(this.orgChartData);
this.layerTree(this.showOrgChartData, 'childList', layer, 1);
this.orgChartObject = new OrgChart(this.$refs.orgChartDom, this.showOrgChartData, this.direction);
},
// 机构图--导出按钮
downloadPic() {
if (this.orgChartObject) {
this.orgChartObject.exportPNG({
filename: "机构图.png",
openInNewTab: false,
expandChildren: true,
margin: [10,20,10,20],
});
}
},
/**
* 从树形数据中按层级取数据
* @param {Array} list 树形数据
* @param {String} childKey 子节点字段
* @param {Number} layer 目标成绩
* @param {Number} currentLayer 当前层级
*/
layerTree(list, childKey, layer, currentLayer) {
if (list && Array.isArray(list) && list.length > 0) {
list.forEach(item => {
if (currentLayer < layer) {
this.layerTree(item[childKey], childKey, layer, currentLayer + 1)
}else{
delete item[childKey]
}
});
}
},
/**
* 深拷贝,返回拷贝后的对象
* @param {Object} target 要深拷贝的对象
*/
deepClone (target) {
let result, targetType = this.getObjClass(target);
if(targetType === 'Object'){
result = {};
}else if(targetType === 'Array'){
result = [];
}else {
return target;
}
for(let key in target){
let value = target[key];
// eslint-disable-next-line no-constant-condition
if(this.getObjClass(value) === "Object" || 'Array'){
result[key] = this.deepClone(value);
}else {
result[key] = target[key];
}
}
return result;
},
//检测数据类型
getObjClass(obj) {
return Object.prototype.toString.call(obj).slice(8, -1);
},
}
};
</script>