子组件(递归组件,无限下级):
<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">
<vDialog v-show="showDialog" :dialog-option="dialogOption" ref="dialog"></vDialog>
<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="showNewNode()" >添加子部门</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 class="modal-newNode" v-if="showNodeModal">
<div class="modal-newNode-top">
<div style="display: inline-block;"><span class="modal-newNode-name">新建部门名:</span></div>
<div style="display: inline-block;"><input class="modal-newNode-input" type="text" v-model="newNodeName"></div>
</div>
<div class="modal-newNode-buttons"><button class="modal-newNode-button" @click="addChildNode()">确定</button></div>
<div class="modal-newNode-buttons"><button class="modal-newNode-button" @click="hideNodeModal()">取消</button></div>
</div>
</div>
</div>
</div>
<footBottom>
</footBottom>
</div>
</template>
<script>
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 vDialog from '../../components/utils/commonModalTen.vue'
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,
showNodeModal:false,
showDialog:false,
master:0,
dialogOption:[
{
title:'',
text:'',
happiness:'',
sad:'',
department:null
}
],
newNodeName:'',
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()
/* window.onbeforeunload = function(event) {
return "确定退出吗";
}*/
},
created(){
},
components: {
headTop,
menuLeft,
footBottom,
vDialog,
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);
}
}
}
if(!('children' in nodes[0])){
nodes[0].children = []
}
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){
}
})
$.ajax({
type:'POST',
url:global.urlPath+'getDepartmentMaster',
data:{
enterpriseId:sessionStorage.getItem("enterpriseId")
},
success:function(data){
let result=$.parseJSON(data)
that.master = result.master
}
})
},
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
},
hideNodeModal(){
this.showNodeModal=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()
},
showNewNode(){
this.showNodeModal=true
this.showModal=false
},
addChildNode(){
if(this.master == 0){
this.dialogOption[0].text = '请为当前部门添加职位'
this.dialogOption[0].happiness = false
this.dialogOption[0].sad = false
this.dialogOption[0].departmentId = this.treeNode.id
this.showDialog = true
}else {
for (let i = 0; i < this.toList.length; i++) {
if (this.toList[i] === this.treeNode) {
let newNode = {
id: null,
name: this.newNodeName,
grade: this.treeNode.grade + 1,
enterprise_id: sessionStorage.getItem("enterpriseId"),
super_id: this.treeNode.id
}
let that = this
/* $.ajax({
type:'POST',
url:global.urlPath+'insertDepartment',
async: false,
data:newNode,
success:function(data){
let result=$.parseJSON(data)
newNode.id = result.departmentId
delete newNode.enterprise_id
},
error:function(e){
}
})*/
that.toList.push(newNode)
//this.$router.push('/addNewOffer')
this.hideNodeModal()
//TODO 添加孩子节点
that.dialogOption[0].text = '请为新部门添加职位'
that.dialogOption[0].happiness = false
that.dialogOption[0].sad = false
that.dialogOption[0].department = newNode
that.showDialog = true
}
}
this.organizationTree = JSON.parse(JSON.stringify(this.ListtoTree[0]))
this.hideModal()
}
},
subSelfNode(){
let subList = [];
if(this.treeNode.id != 1){
subList.push(this.treeNode.id)
}else{
this.dialogOption[0].text = '根节点不可以删除'
this.dialogOption[0].happiness = false
this.dialogOption[0].sad = true
this.dialogOption[0].department = null
this.showDialog = true
return false
}
subNode(this.toList,this.treeNode.id)
function subNode(Node,id){
for(let i=0;i<Node.length;i++){
if(Node[i].super_id == id){
let superId = Node[i].id
subNode(Node,superId)
//Node.splice(i,1)
subList.push(Node[i].id)
}
}
}
for(let i=0;i<subList.length;i++){
for(let j=0;j<this.toList.length;j++){
if(this.toList[j].id === subList[i]){
this.toList.splice(j,1)
}
}
}
//TODO 删除
let ids="";
for(let i=0;i<subList.length;i++){
if(i==0){
ids=subList[i];
}else{
ids += ','+subList[i];
}
}
let that = this
$.ajax({
type:'POST',
url:global.urlPath+'deleteDepartment',
data:{
ids:ids
},
success:function(data){
let result=$.parseJSON(data)
},
error:function(e){
}
})
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 scoped>
.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:5px;
font-weight:600;
font-size:16px;
color:#333333;
vertical-align: middle;
display:inline-block;
}
.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-newNode{
width:300px;
height:150px;
position:absolute;
left:45%;
top:20%;
background-color:#ffffff;
text-align: center;
z-index:1000;
}
.modal-newNode-top{
margin-top:30px;
}
.modal-newNode-name{
width: 60px;
height: 36px;
line-height: 36px;
}
.modal-newNode-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-newNode-buttons{
display:inline-block;
}
.modal-newNode-button{
width: 60px;
height: 32px;
display: inline-block;
font-size: 14px;
color: #0099ff;
border: 1px solid #0099ff;
border-radius: 16px;
background-color: #fff;
text-align: center;
margin: 0px 20px;
margin-top: 30px;
cursor: pointer;
}
.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>