子组件(递归组件,无限下级):
<template> <div ref="rootNode" :class="[isGeneral==0?rootClassNode:[isGeneral==1?firstClassNode:generalClassNode]]"> <div @click.stop="toUpdateNode(treeRoot)" :class="[isGeneral==0?rootClass:[isGeneral==1?firstClass:generalClass]]" :style="{'cursor':changeFlag?'pointer':'auto'}"> <span v-if="isGeneral<2"><img src="/static/img/test.0affb86.png" alt=""></span> <span >{{treeRoot.name}}</span> <span :class="[isGeneral<2?nomalNum:extraNum]">30人</span> </div> <div v-if="isGeneral==0" class="centerline" :style="{width:centerlineWidth+'px',marginLeft:centerlineLeft+'px'}"></div> <recursive-component-tree v-for="item of treeRoot.children" :key="item.id" v-if="treeRoot.children" :tree-root="item" :changeFlag="changeFlag" v-on:toChangeNode="toUpdateNode"></recursive-component-tree> </div> </template> <script> import $ from 'jquery' export default { name: "recursive-component-tree", props: { treeRoot:Object, changeFlag:Boolean }, data(){ return{ rootClass:'rootClass', firstClass:'firstClass', generalClass:'generalClass', rootClassNode:'rootClassNode', firstClassNode:'firstClassNode', generalClassNode:'generalClassNode', nomalNum:'nomalNum', extraNum:'extraNum', centerlineWidth:0, centerlineLeft:0 } }, updated(){ }, mounted(){ if($(this.$refs.rootNode).attr('class') == 'rootClassNode' && this.isGeneral==0){ let firstLeft = $($(this.$refs.rootNode).children('.firstClassNode').get(0)).position().left let lastLeft = $(this.$refs.rootNode).children(':last-child').position().left this.centerlineWidth = lastLeft-firstLeft this.centerlineLeft = firstLeft+90 } }, computed:{ isGeneral:function () { return this.treeRoot.grade; } }, methods: { toUpdateNode(treeRoot) { if(this.changeFlag){ this.$emit('toChangeNode', treeRoot) } } } } </script> <style scoped> img{ width: 45px; height: 45px; border-radius: 22px; display: inline-block; vertical-align: middle; } .tree{ text-align:left; padding-left:20px; } .rootClass{ width:161px; height:72px; background-color:#eff8ff; line-height: 72px; border-radius: 5px; margin:0 auto; margin-bottom: 23px; } .rootClass:after{ content: ""; width: 1px; height: 23px; background-color: #0099ff; display: block; margin-left: 80px; } .rootClassNode{ height: 500px; position:relative; } .firstClass{ width:161px; height:72px; background-color:#eff8ff; line-height: 72px; border-radius: 5px; text-align: center; } .firstClassNode{ display:inline-block; vertical-align:top; padding:0 10px; text-align: left; } .firstClassNode:before{ content: ""; width: 1px; height: 23px; background-color: #0099ff; display: block; margin-left: 80px; } .generalClass{ font-size: 14px; line-height: 28px; } .generalClass:before{ content:'\2022'; font-size: 18px; color: #0099ff; padding: 5px; } .generalClassNode{ padding-left: 20px; } .centerline{ height:1px; background-color: #0099ff; margin:0 auto; } .nomalNum{ font-size: 12px; color: #666666; display:inline-block; } .extraNum{ padding-left:8px; } .extraNum:before{ content:'('; } .extraNum:after{ content:')'; } /* .firstClass:after{ display:block;clear:both;content:"";visibility:hidden;height:0 }*/ </style>
父组件:
<template> <div class="all"> <headTop :isActive_2="isActive_2"> </headTop> <div class="organization-main"> <div class="menuLeft"> <menuLeft :isActive_9="isActive_9"></menuLeft> </div> <div class='organization-index'> <div class='organization-index-top'> <div class='main-top-title'> <img class='main-top-title-img' src='../../assets/roundedRectangle.png'/> <div class='main-top-title-text'>组织结构</div> </div> <div class='main-top-change' @click="toEditStructure()"> <img src='../../assets/informationEdit.png'/> <div class='main-top-change-text' >{{changeOrSave}}</div> </div> </div> <div style="overflow: scroll;white-space:nowrap"> <RecursiveComponentTree :tree-root="organizationTree" v-if="updateRoot" :changeFlag="childrenFlag" v-on:toChangeNode="handleChangeNode"></RecursiveComponentTree> <div class='modal-total' v-if="showModal" v-on:click="hideModal()"></div> <!--修改模态框--> <div class="modal" v-if="showModal"> <div class='modal-top'> <div class="modal-top-button" v-on:click="hideModal()"></div> <div class='modal-top-right'> <div class='modal-top-right-wrong'></div> </div> </div> <div class="modal-buttons"> <div class="modal-updateNode"><span class="modal-NodeName">修改部门名:</span><input type="text" v-model="changeName" /></div> <div class='modal-add-button' @click="addChildNode()" >添加子部门</div> <div class='modal-sub-button' @click="subSelfNode()">删除当前部门</div> </div> <div class='modal-four'> <div class='modal-four-button' @click="submitChange()">确定</div> <div class='modal-four-button' @click="hideModal()">取消</div> </div> </div> </div> </div> </div> <footBottom> </footBottom> </div> </template> <script> //import Vue from 'vue' import headTop from '../../components/header/header.vue' import menuLeft from '../../components/enterpriseSetting/enterpriseSetting' import footBottom from '../../components/footer/footer.vue' import RecursiveComponentTree from '../../components/utils/RecursiveComponentTree' import $ from 'jquery' export default{ data(){ return{ departLength:0, fullWidth:0, leftWidth:0, botWidth:0, isActive_9:true, isActive_2:true, childrenFlag:false, changeOrSave:'修改', showModal:false, changeName:'', treeNode:Object, updateRoot:true, toList:Object, organizationTree:{} /* organizationTree:{ id:1, name:'CEO', super_id:0, grade:'0', children:[ { id:11, name:'部门1', super_id:1, grade:'1', children:[ { id:111, name:'部门1-1', grade:'2', super_id:11, children:[ { id:1111, name:'部门1-1-1', grade:'3', super_id:111, children:[] } ] } ] }, { id:12, name:'部门2', grade:'1', super_id:1, children:[ { id:121, name:'部门2-1', super_id:12, grade:'2', children:[ { id:1211, name:'部门2-1-1', super_id:121, grade:'3', children:[ { id:12111, name:'部门2-1-1-1', super_id:1211, grade:'4', children:[] } ] }, { id:1212, name:'部门2-1-2', super_id:121, grade:'3', children:[] } ] }, { id:122, name:'部门2-2', super_id:12, grade:'2', children:[] } ] }, { id:13, name:'部门3', super_id:1, grade:'1', children:[ { id:131, name:'部门3-1', super_id:13, grade:'2', children:[] } ] } ] }*/ } }, mounted(){ this.initDate() }, created(){ }, components: { headTop, menuLeft, footBottom, RecursiveComponentTree }, computed: { TreetoList(){ let listData = []; transferTreeData(JSON.parse(JSON.stringify([this.organizationTree]))) function transferTreeData (sourceData) { sourceData.forEach( function(node, nodeIndex) { if(node.children.length > 0) transferTreeData(node.children) listData.push(node) }) } return listData; }, ListtoTree() { let list = JSON.parse(JSON.stringify(this.toList)) //let list = this.toList let myId = 'id' let pId = 'super_id' function exists(list, parentId) { //判断是否有父亲 for (var i = 0; i < list.length; i++) { if (list[i][myId] == parentId) return true } return false } var nodes = []; for (var i = 0; i < list.length; i++) { var row = list[i] if (!exists(list, row[pId])) { nodes.push(row) } } var toDo = []; for (var i = 0; i < nodes.length; i++) { toDo.push(nodes[i]); } while (toDo.length) { var node = toDo.shift() for (var i = 0; i < list.length; i++) { var row = list[i] if (row[pId] == node[myId]) { if (node.children) { node.children.push(row) } else { node.children = [row] } row.children = [] toDo.push(row); } } } return nodes; } }, methods: { initDate(){ let that = this $.ajax({ type:'POST', url:global.urlPath+'getAllDepartments', data:{ enterpriseId:sessionStorage.getItem("enterpriseId") }, success:function(data){ let result=$.parseJSON(data) that.organizationTree = result.departments[0] }, error:function(e){ } }) }, toEditStructure(){ this.childrenFlag = !this.childrenFlag this.childrenFlag?this.changeOrSave = '退出编辑':this.changeOrSave = '修改' }, handleChangeNode(Nodedata){ //console.log(Nodedata) this.showModal = true this.toList = JSON.parse(JSON.stringify(this.TreetoList)) this.toList.forEach(function(Listnode){ delete Listnode.children }) this.treeNode = this.toList.find((e)=>e.id === Nodedata.id) this.changeName = this.treeNode.name }, hideModal(){ this.showModal=false }, submitChange(){ for(let i=0;i<this.toList.length;i++){ if(this.toList[i] === this.treeNode){ this.$set(this.toList[i], 'name', this.changeName) let that = this $.ajax({ type:'POST', url:global.urlPath+'updateDepartmentName', data:{ id:that.toList[i].id, name:this.changeName }, success:function(data){ let result=$.parseJSON(data) console.log(result) }, error:function(e){ } }) } } //TODO 修改名称 this.organizationTree = JSON.parse(JSON.stringify(this.ListtoTree[0])) this.hideModal() }, addChildNode(){ for(let i=0;i<this.toList.length;i++){ if(this.toList[i] === this.treeNode){ let newNode = { id:null, name:"新增子部门", grade:this.treeNode.grade+1, super_id:this.treeNode.id } let that = this $.ajax({ type:'POST', url:global.urlPath+'updateDepartmentName', data:{ id:that.toList[i].id, name:this.changeName }, success:function(data){ let result=$.parseJSON(data) console.log(result) }, error:function(e){ } }) this.toList.push() //TODO 添加孩子节点未提交 } } this.organizationTree = JSON.parse(JSON.stringify(this.ListtoTree[0])) this.hideModal() }, subSelfNode(){ let flag = true for(let i=0;i<this.toList.length;i++){ if(this.toList[i] === this.treeNode){ for(let j=0;j<this.toList.length;j++){ if(this.toList[j].super_id === this.treeNode.id){ flag = false } } if(flag === true){ //TODO 删除未提交 this.toList.splice(i,1) }else{ //TODO 有子节点不可删除 console.log("error") } } } this.organizationTree = JSON.parse(JSON.stringify(this.ListtoTree[0])) this.hideModal() } }, watch:{ organizationTree:{ handler: function(curVal,oldVal) { this.updateRoot = false let _this = this setTimeout(function (){ _this.updateRoot = true }, 10) }, deep:true //深度watch } } } </script> <style> .all{ position: relative; min-height:100%; padding-bottom: 200px; } .organization-main:after,.organization-index-top:after{ display:block;clear:both;content:"";visibility:hidden;height:0 } .organization-main{ width:1200px; margin:130px auto 70px; font-size:14px; /*display:flex;*/ /*flex-direction:row;*/ font-size:14px; text-align:center; } .menuLeft{ float:left; } .organization-index{ background-color:#ffffff; width:969px; height:650px; border-radius:5px; padding-bottom:20px; margin-left:20px; float:right; } .organization-index-top{ /*display:flex;*/ /*flex-direction:row;*/ } .main-top-title{ padding-left:20px; padding-top:20px; /*width:1050px;*/ height:100px; text-align: left; float:left; /*display:flex;*/ /*flex-direction:row;*/ } .main-top-title-img{ width:17px; height:22px; display:inline-block; vertical-align: middle; } .main-top-title-text{ padding-left:15px; font-weight:600; font-size:16px; color:#333333; display:inline-block; vertical-align: middle; } .main-top-change{ padding-top:20px; width:150px; float:right; /*display:flex;*/ /*flex-direction:row;*/ font-size:14px; color:#0099ff; cursor: pointer; } .main-top-change img{ width:20px; height:20px; display:inline-block; vertical-align: middle; } .main-top-change-text{ padding-left:10px; display:inline-block; vertical-align: middle; } .organization-area{ /*width:969px;*/ padding-left:40px; padding-top:20px; padding-right:20px; /*overflow-x: scroll;*/ /*overflow-y:hidden;*/ white-space: nowrap; height:480px; scrollbar-face-color: #20a774; scrollbar-shadow-color: #20a774; scrollbar-track-color: #ccc; scrollbar-arrow-color: #ddd; } ::-webkit-scrollbar-thumb{ background-color:#018EE8; height:50px; outline-offset:-2px; outline:8px solid #fff; -webkit-border-radius:4px; } ::-webkit-scrollbar-track-piece{ background-color:#fff; -webkit-border-radius:0; } ::-webkit-scrollbar{ width:8px; height:8px; } ::-webkit-scrollbar-thumb:hover{ background-color:#FB4446; height:50px; -webkit-border-radius:4px; } .organization-area-top-out{ color:#ffffff; /*displaY:flex;*/ /*flex-direction:row;*/ } .organization-area-top-left{ color:#ffffff; display:inline-block; } .organization-area-top{ /*dispaly:flex;*/ /*flex-direction:column;*/ margin: 0 auto; display:block; width:161px; } .organization-area-top-leader{ width:161px; height:72px; background-color:#eff8ff; color:#333333; /*display:flex;*/ /*flex-direction:row;*/ line-height:72px; border-radius:5px; } .organization-area-top-leader img{ width:45px; height:45px; margin-top:15px; margin-left:10px; border-radius:22px; display: inline-block; } .organization-area-top-leader-name{ font-size:16px; margin-left:10px; display: inline-block; vertical-align: top; } .organization-area-top-leader-count{ font-size:12px; color:#666666; margin-left:10px; display: inline-block; vertical-align: top; } .organization-area-top-leader-line{ width:1px; height:23px; background-color:#0099ff; margin-left:80px; } .organization-area-middle{ min-width:1px; height:1px; background-color:#0099ff; margin: 0 auto; } .organization-area-bot{ white-space:nowrap; } .organization-area-bot-index{ /*width:161px;*/ vertical-align: top; margin-right:20px; display:inline-block; } .organization-area-bot-line{ width:1px; height:29px; margin-left:80px; background-color:#0099ff; } .organization-area-bot-depart{ /*width:161px;*/ height:72px; line-height:72px; color:#333333; background-color:#eff8ff; /*display:flex;*/ /*flex-direction:row;*/ margin-right:20px; border-radius:5px; } .organization-area-bot-depart img{ width:45px; height:45px; border-radius:22px; margin-top:15px; margin-left:10px; display: inline-block; } .organization-area-bot-depart-name{ font-size:16px; margin-left:10px; display: inline-block; vertical-align:top; } .organization-area-bot-depart-count{ font-size:12px; color:#666666; margin-right:10px; display: inline-block; vertical-align:top; } .organization-area-bot-depart-position{ /*width:161px;*/ /*display:flex;*/ /*flex-direction:row;*/ color:#333333; font-size:14px; white-space:nowrap; margin-top:5px; margin-bottom:5px; } .organization-area-bot-depart-position-little{ width:5px; height:5px; border-radius:2.5px; background-color:#0099ff; margin-top:10px; margin-left:5px; display:inline-block; } .organization-area-bot-depart-position-name{ margin-left:5px; display:inline-block; } .organization-area-bot-depart-position-count{ margin-left:5px; display:inline-block; } .modal-total{ width: 100%; height: 100%; position: fixed; top: 0; left: 0; background: #000; z-index: 90; opacity: 0.2; } .modal{ width:400px; height:300px; background-color:#ffffff; z-index:999; position:absolute; left:45%; top:20%; text-align: center; } .modal-top { width: 10px; height: 10px; margin-top: 18px; margin-left: 365px; } .modal-top-button{ width: 22px; height: 22px; position: absolute; top: 8px; right: 9px; cursor: pointer; z-index: 1; } .modal-four{ margin-top:50px; } .modal-add-button{ width:160px; height:32px; margin: 0 auto; margin-top: 30px; font-size:14px; color:#0099ff; border:1px solid #0099ff; border-radius:16px; text-align:center; padding-top:5px; cursor: pointer; } .modal-sub-button{ width:160px; height:32px; margin: 0 auto; margin-top: 30px; font-size:14px; color:#0099ff; border:1px solid #0099ff; border-radius:16px; text-align:center; padding-top:5px; cursor: pointer; } .modal-four-button{ width:60px; height:32px; display:inline-block; font-size:14px; color:#0099ff; border:1px solid #0099ff; border-radius:16px; text-align:center; padding-top:5px; margin:0px 20px; cursor: pointer; } .modal-updateNode{ text-align: left; padding-left: 37px; margin-top: 10px; } .modal-updateNode input{ background-color:#edf6fc; border-radius:18px; width:160px; height:32px; padding:0px 10px; line-height:36px; border:1px solid #a4dbff; outline: 0 none !important; } .modal-NodeName{ width:60px; height:36px; line-height:36px; } .modal-top-right-wrong{ width: 30px; height:2px; background: #868686; transform: rotate(45deg); } .modal-top-right-wrong:after{ content:''; display:block; width: 30px; height:2px; background: #868686; transform: rotate(-90deg); } </style>