0


vue2--基于zm-tree-org实现公司部门组织架构图

1.安装zm-tree-org

npm i zm-tree-org -S

2.引入使用

import Vue from 'vue';import ZmTreeOrg from 'zm-tree-org';import"zm-tree-org/lib/zm-tree-org.css";

Vue.use(ZmTreeOrg);

3.个人需求

组织架构图中:部门可拖动更改所属部门,可增加部门下节点,删除当前部门节点,查看当前部门节点的所有所属节点,编辑当前部门节点,同级部门节点可进行左右换位移动

4.实现效果

基于zm-tree-org实现组织架构图
整体如图所示:
在这里插入图片描述
因为需求没有用插件里面的自定义右键菜单(define-menus)
实现效果如图所示:
请添加图片描述

5.代码实现

<template><div><divid="mainContent"style="height: 800px;border:1px solid #eee"><zm-tree-orgref="treeRefs"node-key="id":data="data":horizontal="horizontal":collapsable="collapsable":node-draggable="true":only-one-node="onlyOneNode":clone-node-drag="cloneNodeDrag":tool-bar="toolBar":default-expand-level="5":define-menus="defineMenus":node-delete="handleOnNodeDelete"><!-- 利用插槽实现自定义节点 --><templatev-slot="{node}"><divclass="card-main"><div:class="node.isRoot?'top-position top-position-root':'top-position'"><divv-if="!node.isRoot"@click="handleChangeNode($event,node,'before')"><iclass="el-icon-arrow-left"/></div><div>{{ node.department }}</div><divv-if="!node.isRoot"@click="handleChangeNode($event,node,'after')"><iclass="el-icon-arrow-right"/></div></div><!-- <div class="top-position">{{ node.department }}</div> --><divclass="p-title">{{ node.operation }}</div><divclass="p-people"><divclass="p-peopleName"><divv-if="node.name"class="p-cicle">{{ getNameCicle(node.name,node) }}</div><templatev-else><divclass="p-cicle p-cicle-empty"><span>虚位</span><span>以待</span></div></template><divclass="p-name">{{ node.name }}</div></div></div><div:class="node.isRoot ? 'operation-btn operation-root-btn' :'operation-btn'"><div@click="onNodeHandleBtn('add',node)"><iclass="iconfont icon-tianjia"/></div><!-- root节点只支持添加 --><templatev-if="!node.isRoot"><el-popover:ref="`popover-${node.id}`"placement="bottom"trigger="click":append-to-body="false":popper-options="{ boundariesElement: 'body', gpuAcceleration: false,}"popper-class="customCont"><divclass="customCont-main"><divclass="customCont-main-close"@click="closePopover(`popover-${node.id}`)"><iclass="el-icon-close"/></div><divv-if="node.children && node.children.length"class="organization"><divv-for="(item,index) in node.children":key="index"class="organization-list"><divclass="organization-list-top">{{ item.department }}</div><divclass="organization-list-content">{{ item.name }}</div></div></div><divv-elseclass="organization organization-empty"><divclass="organization-empty-info">
                        暂无消息
                      </div></div></div><divslot="reference"><iclass="iconfont icon-zuzhijiagou"/></div></el-popover><div@click="onNodeHandleBtn('edit',node)"><iclass="iconfont icon-bianjishuru-xianxing"/></div><div@click="onNodeDeleteBtn(node)"><iclass="iconfont icon-shanchu"/></div></template></div></div></template></zm-tree-org></div><el-dialog:title="dialogType==='add' ? '新增部门' :'编辑部门'":visible.sync="dialogVisible"custom-class="custom-dialog"center><el-inputv-model="departmentName"autocomplete="off"placeholder="部门名称(50字内)"/><divslot="footer"class="dialog-footer"><el-buttontype="primary"@click="handelDepartment">保 存</el-button></div></el-dialog></div></template><script>import{
  getParentNode,
  getBeforeBrotherNode,
  getAfterBrotherNode,
  changeBeforeNode,
  changeAfterNode,
  handleOnNodeDelete,}from"./common";exportdefault{data(){return{toolBar:{scale:false,},data:{id:1,department:"某某某死扣的公司",operation:"管理员",name:"哈哈哈哈",isRoot:true,children:[{id:2,pid:1,department:"产品研发部",operation:"研发主管",name:"张三",children:[{id:3,pid:2,department:"科技创新中心",operation:"研发-前端",name:"前端哈",},],},{id:4,pid:1,department:"销售部",operation:"销售主管",name:"李四",children:[{id:5,pid:4,department:"销售一部",operation:"销售1",name:"李四1",},{id:6,pid:4,department:"销售二部",operation:"销售2",name:"李四2",},],},{id:7,pid:1,department:"财务部",operation:"财务总监",name:"王二",children:[{id:8,pid:7,department:"销售一部",operation:"销售1",name:"李四1",},{id:9,pid:7,department:"销售二部",operation:"销售2",name:"李四2",children:[{id:10,pid:9,department:"销售一部",operation:"销售1",name:"李四1",},{id:11,pid:9,department:"销售二部",operation:"销售2",name:"李四2",children:[{id:12,pid:11,department:"销售一部",operation:"销售1",name:"李四1",},{id:13,pid:11,department:"销售二部",operation:"销售2",name:"李四2",},],},],},],},],},horizontal:false,// 是否横向collapsable:true,// 是否可展开收起onlyOneNode:false,// 是否仅拖动当前节点cloneNodeDrag:false,// 是否拷贝节点拖拽expandAll:true,//dialogVisible:false,// 弹框显隐dialogType:"",// 弹框类型departmentName:"",// 部门名称nodeTree:"",// 当前点击的nodeTree};},methods:{
    handleOnNodeDelete,// 隐藏左键菜单defineMenus(){return[];},// 截取名字getNameCicle(name, node){const len = name && name.length;return name && len && name.slice(len -2, len);},// 关闭组织架构closePopover(refs){
      document.body.click();},// 增加/编辑部门节点onNodeHandleBtn(type, node){this.dialogType = type;this.dialogVisible =true;this.nodeTree = node;if(type ==="edit"){this.departmentName = node.department;}},// 弹框保存按钮handelDepartment(){// 添加if(this.dialogType ==="add"){const params ={id: Math.ceil(Math.random()*1000+100),pid:this.nodeTree.id,level:this.nodeTree.level ||-1,operation:"职位",name:"",department:this.departmentName,};if(Array.isArray(this.nodeTree["children"])){this.nodeTree["children"].push(params);}else{this.$set(this.nodeTree,"children",[].concat(params));}// 编辑}else{this.$set(this.nodeTree,"department",this.departmentName);}this.dialogVisible =false;this.departmentName ="";},// 删除部门节点onNodeDeleteBtn(node){const _this =this;if(node.root){// 根节点不允许删除this.$Message.warning("根节点不允许删除!");returnfalse;}// 部门无职位信息const tips =
        node.children && node.children.length
          ?`<div>系统检测到该部门下仍有相关职位信息,请转移/删除对应职位后再试!</div>`:`<div>您确定要删除部门:<span style="color:#0469c0">${node.department}</span> 吗?</div>`;
      _this
        .$alert(tips,"提示",{dangerouslyUseHTMLString:true,customClass:"deleteDailog",showCancelButton:!(node.children && node.children.length),showConfirmButton:!(node.children && node.children.length),confirmButtonText:"确定",cancelButtonText:"取消",}).then(async()=>{try{const parentNode =getParentNode(this.data,"id", node.pid);handleOnNodeDelete(this, node, parentNode);}catch(error){
            _this.$message({type:"error",message:"操作失败,请重试!",});}}).catch(()=>{});},// 同级部门交换节点handleChangeNode(e, node, type){
      e.stopPropagation();const resultData =[].concat(this.data);if(type ==="before"){// 判断是否有前面兄弟节点const isHasBeforNode =getBeforeBrotherNode(resultData, node, type);if(!isHasBeforNode?.id){// 前面无兄弟节点returnfalse;}else{// 有兄弟节点 进行交换处理const _data = Object.assign({},{...changeBeforeNode(resultData, isHasBeforNode, node)})[0];this.$nextTick(()=>{this.$set(this.data,"children",JSON.parse(JSON.stringify(_data.children)));});this.$message({message:"操作成功!",type:"success",});}}else{// 判断后面是否有兄弟节点const isHasAfterNode =getAfterBrotherNode(resultData, node, type);if(!isHasAfterNode?.id){returnfalse;}else{const _data = Object.assign({},changeAfterNode(resultData, isHasAfterNode, node))[0];this.$nextTick(()=>{this.$set(this.data,"children",JSON.parse(JSON.stringify(_data.children)));});this.$message({message:"操作成功!",type:"success",});}}},},};</script><stylelang="scss"scoped>/* 每个节点样式 */.card-main{min-width: 15vw;font-size: 12px;.top-position{background-color: #899cc1;color: #ffffff;padding: 10px;display: flex;justify-content: space-between;align-items: center;}.top-position-root{justify-content: center;}.p-title{color: #868686;padding: 15px 0;}.p-people{display: flex;justify-content: space-evenly;.p-peopleName{display: flex;flex-direction: column;align-items: center;}.p-cicle{background:rgba(4, 45, 124, 0.5);color: #ffffff;border-radius: 42px 42px 42px 42px;width: 48px;height: 48px;line-height: 48px;font-size: 14px;margin-bottom: 5px;}.p-cicle-empty{display: flex;flex-direction: column;justify-content: center;line-height: 20px;}.p-name{color: #000000;min-height: 13.8px;}}.operation-btn{display: flex;justify-content: space-between;background: #f9f9f9;height: 40px;padding: 0 10px;align-items: center;margin-top: 15px;position: relative;}.operation-root-btn{justify-content: flex-end;}}/* 节点操作按钮 */.iconfont{font-size: 20px;color: #878787;}/* popover样式 */.operation-btn ::v-deep.customCont{background: #e2f4e9;margin-top: 15px;.popper__arrow::after{border-bottom-color: #e2f4e9;}}.customCont-main{position: relative;&-close{position: absolute;z-index: 10;top: -5px;right: 0;.el-icon-close:before{font-size: 16px;font-weight: 600;}}}.organization{display: flex;flex-wrap: wrap;justify-content: space-between;align-items: center;padding: 0 18px 18px;min-width: 30vw;}.organization-list{font-size: 12px;min-height: 80px;border: 1px solid rgba(0, 0, 0, 0.06);margin-top: 18px;width: 45%;&-top{background:rgba(42, 178, 98, 0.88);color: #ffffff;padding: 10px;text-align: center;}&-content{padding: 20px;color: #000000;background: #ffffff;}}.organization-empty{min-width: 20vw;min-height: 50px;padding: 0;&-info{width: 100%;text-align: center;font-size: 12px;color: #000000;}}</style><stylelang="scss">/* 新增部门弹框 */.custom-dialog{width: 18%;.el-dialog__header{border-bottom: 1px solid #e8e8e8;.el-dialog__title{font-size: 14px;}.el-dialog__headerbtn .el-dialog__close{font-weight: 600;}}.el-dialog__body{.el-input.is-active .el-input__inner,
    .el-input__inner:focus{border-color: #042d7c;outline: 0;}}.dialog-footer{text-align: center;.el-button{background: #042d7c;color: #ffffff;}}.error-tips{font-size: 12px;color: #f56c6c;line-height: 1;padding-top: 4px;position: absolute;}}.tree-org-node__inner:hover{box-shadow: 2px 2px 5px rgba(4, 45, 124, 0.55);}/* 删除部门弹框 */.deleteDailog{min-width: 30vw;.el-message-box__header{border-bottom: 1px solid #e8e8e8;}.el-message-box__message{text-align: center;}/* .el-message-box__btns {
    .el-button:focus,
    .el-button:hover {
      color: #606266;
      border-color: #dcdfe6;
      background-color: #ffffff;
    }
    .el-button--primary {
      background-color: #042d7c;
      border-color: #042d7c;
    }
    .el-button--primary:focus,
    .el-button--primary:hover {
      background-color: #042d7c;
      color: #ffffff;
      border-color: #042d7c;
    }
  } */}</style>

common.js中的方法

// 递归查找父节点/**
 *
 * @param {*} treeData 整个组织架构数据
 * @param {*} key id键值名
 * @param {*} pid 父节点id
 * @returns
 */exportconstgetParentNode=(treeData, key, pid)=>{if(treeData[key]=== pid){return treeData;}elseif(Array.isArray(treeData.children)){const list = treeData.children;for(let i =0, len = list.length; i < len; i++){const row = list[i];const pNode =getParentNode(row, key, pid);if(pNode){return pNode;}}}}// 查找前面兄弟节点/**
 *
 * @param {*} treeData 整个组织架构数据
 * @param {*} nowNode 当前节点
 * @returns
 */exportconstgetBeforeBrotherNode=(treeData, nowNode)=>{for(let i =0, len = treeData.length; i < len; i++){if(treeData[i].id === nowNode.id){if(i >0){return treeData[i -1];}else{// 没有前面兄弟节点returnfalse;}}elseif(treeData[i].children){const isHasBeforNode =getBeforeBrotherNode(treeData[i].children, nowNode);if(isHasBeforNode)return isHasBeforNode;}}}// 和前面兄弟节点进行交换/**
 *
 * @param {*} treeData 整个组织架构数据
 * @param {*} beforeNode 前兄弟节点
 * @param {*} nowNode 当前节点
 * @returns
 */exportconstchangeBeforeNode=(treeData, beforeNode, nowNode)=>{for(let i =0, len = treeData.length; i < len; i++){if(treeData[i].id === nowNode.id){let obj ={};
      obj = treeData[i];
      treeData[i]= beforeNode;
      treeData[i -1]= nowNode;break;}elseif(treeData[i].children){changeBeforeNode(treeData[i].children, beforeNode, nowNode);}}return treeData;}// 查找后面的兄弟节点/**
 *
 */exportconstgetAfterBrotherNode=(treeData, nowNode)=>{for(let i =0, len = treeData.length; i < len; i++){if(treeData[i].id === nowNode.id){if(i < treeData.length -1){return treeData[i +1];}else{// 没有后面兄弟节点returnfalse;}}elseif(treeData[i].children){const isHasAfterNode =getAfterBrotherNode(
        treeData[i].children,
        nowNode
      );if(isHasAfterNode)return isHasAfterNode;}}}// 和后面兄弟节点进行交换/**
 *
 * @param {*} treeData 整个组织架构数据
 * @param {*} beforeNode 后兄弟节点
 * @param {*} nowNode 当前节点
 * @returns
 */exportconstchangeAfterNode=(treeData, afterNode, nowNode)=>{for(let i =0, len = treeData.length; i < len; i++){if(treeData[i].id === nowNode.id){let obj ={};
      obj = treeData[i];
      treeData[i]= afterNode;
      treeData[i +1]= nowNode;break;}elseif(treeData[i].children){changeAfterNode(treeData[i].children, afterNode, nowNode);}}return treeData;}// 处理删除节点(调用原组件事件)/**
 *
 * @param {*} nowNode  当前节点
 * @param {*} parentNode 父节点
 */exportconsthandleOnNodeDelete=(_, nowNode, parentNode)=>{const list = parentNode["children"];for(let i =0, len = list.length; i < len; i++){if(list[i]["id"]=== nowNode["id"]){
      list.splice(i,1);
      _.$emit("on-node-delete", nowNode, parentNode);break;}}}

本文转载自: https://blog.csdn.net/weixin_45324044/article/details/129239043
版权归原作者 可可爱爱的你吖 所有, 如有侵权,请联系我们删除。

“vue2--基于zm-tree-org实现公司部门组织架构图”的评论:

还没有评论