Initiated Organization Chart

pull/41/head
cagataycivici 2019-07-28 14:28:30 +03:00
parent 36e9c27028
commit 1c10bdf86d
7 changed files with 208 additions and 59 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="p-organizationchart p-component"> <div class="p-organizationchart p-component">
<OrganizationChartNode :node="value" /> <OrganizationChartNode :node="value" :templates="$scopedSlots" @node-toggle="onNodeToggle" :collapsedKeys="d_collapsedKeys" :collapsible="collapsible" />
</div> </div>
</template> </template>
@ -12,6 +12,29 @@ export default {
value: { value: {
type: null, type: null,
default: null default: null
},
collapsible: {
type: Boolean,
default: false
},
collapsedKeys: {
type: null,
default: null
}
},
data() {
return {
d_collapsedKeys: this.collapsedKeys || {}
}
},
methods: {
onNodeToggle(key) {
if (this.d_collapsedKeys[key])
delete this.d_collapsedKeys[key];
else
this.d_collapsedKeys[key] = true;
this.d_collapsedKeys = {...this.d_collapsedKeys};
} }
}, },
components: { components: {
@ -49,6 +72,7 @@ export default {
user-select: none; user-select: none;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
cursor: pointer;
} }
.p-organizationchart .p-organizationchart-line-down { .p-organizationchart .p-organizationchart-line-down {

View File

@ -4,9 +4,9 @@
<tr v-if="node"> <tr v-if="node">
<td :colspan="colspan"> <td :colspan="colspan">
<div :class="nodeContentClass" @click="onNodeClick"> <div :class="nodeContentClass" @click="onNodeClick">
{{node.label}} <OrganizationChartNodeTemplate :node="node" :template="templates[node.type]||templates['default']" />
<a v-if="!leaf" tabindex="0" class="p-node-toggler" @click="toggleNode" @keydown.enter="toggleNode"> <a v-if="collapsible && !leaf" tabindex="0" class="p-node-toggler" @click="toggleNode" @keydown.enter="toggleNode">
<i class="p-node-toggler-icon pi" :class="{'pi-chevron-down': node.expanded, 'pi-chevron-up': !node.expanded}"></i> <i class="p-node-toggler-icon pi" :class="{'pi-chevron-down': expanded, 'pi-chevron-up': !expanded}"></i>
</a> </a>
</div> </div>
</td> </td>
@ -24,14 +24,14 @@
</template> </template>
<template v-if="node.children && node.children.length > 1"> <template v-if="node.children && node.children.length > 1">
<template v-for="(child,i) of node.children"> <template v-for="(child,i) of node.children">
<td :key="(child.key || child.label) + '_left'" class="p-organizationchart-line-left" :class="{'p-organizationchart-line-top': !(i === 0)}">&nbsp;</td> <td :key="child.key + '_left'" class="p-organizationchart-line-left" :class="{'p-organizationchart-line-top': !(i === 0)}">&nbsp;</td>
<td :key="(child.key || child.label) + '_right'" class="p-organizationchart-line-right" :class="{'p-organizationchart-line-top': !(i === (node.children.length - 1))}">&nbsp;</td> <td :key="child.key + '_right'" class="p-organizationchart-line-right" :class="{'p-organizationchart-line-top': !(i === (node.children.length - 1))}">&nbsp;</td>
</template> </template>
</template> </template>
</tr> </tr>
<tr :style="childStyle" class="p-organizationchart-nodes"> <tr :style="childStyle" class="p-organizationchart-nodes">
<td v-for="child of node.children" :key="child.key || child.label" colspan="2"> <td v-for="child of node.children" :key="child.key" colspan="2">
<sub-node :node="child" /> <sub-node :node="child" :templates="templates" :collapsedKeys="collapsedKeys" @node-toggle="onChildNodeToggle" :collapsible="collapsible" />
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -39,6 +39,26 @@
</template> </template>
<script> <script>
const OrganizationChartNodeTemplate = {
functional: true,
props: {
node: {
type: null,
default: null
},
template: {
type: null,
default: null
}
},
render(createElement, context) {
const content = context.props.template({
'node': context.props.node
});
return [content];
}
};
export default { export default {
name: 'sub-node', name: 'sub-node',
props: { props: {
@ -53,6 +73,18 @@ export default {
selectable: { selectable: {
type: Boolean, type: Boolean,
default: false default: false
},
templates: {
type: null,
default: null
},
collapsible: {
type: Boolean,
default: false
},
collapsedKeys: {
type: null,
default: null
} }
}, },
methods: { methods: {
@ -60,7 +92,10 @@ export default {
}, },
toggleNode() { toggleNode() {
this.$emit('node-toggle', this.node.key);
},
onChildNodeToggle(key) {
this.$emit('node-toggle', key);
} }
}, },
computed: { computed: {
@ -75,9 +110,15 @@ export default {
}, },
childStyle() { childStyle() {
return { return {
visibility: !this.leaf && this.node.expanded ? 'inherit' : 'hidden' visibility: !this.leaf && this.expanded ? 'inherit' : 'hidden'
} }
},
expanded() {
return this.collapsedKeys[this.node.key] === undefined;
} }
},
components: {
'OrganizationChartNodeTemplate': OrganizationChartNodeTemplate
} }
} }
</script> </script>

View File

@ -9,7 +9,27 @@
<div class="content-section implementation"> <div class="content-section implementation">
<h3 class="first">Advanced</h3> <h3 class="first">Advanced</h3>
<OrganizationChart :value="data1" /> <p>Hierarchical data with zero configuration.</p>
<OrganizationChart :value="data1" :collapsible="true" class="company">
<template #person="slotProps">
<div class="node-header ui-corner-top">{{slotProps.node.data.label}}</div>
<div class="node-content">
<img :src="'demo/images/organization/' + slotProps.node.data.avatar" width="32">
<div>{{slotProps.node.data.name}}</div>
</div>
</template>
<template #default="slotProps">
<span>{{slotProps.node.data.label}}</span>
</template>
</OrganizationChart>
<h3>Basic</h3>
<p>Hierarchical data with zero configuration.</p>
<OrganizationChart :value="data2">
<template #default="slotProps">
<span>{{slotProps.node.data.label}}</span>
</template>
</OrganizationChart>
</div> </div>
<OrganizationChartDoc /> <OrganizationChartDoc />
@ -22,102 +42,110 @@ export default {
data() { data() {
return { return {
data1: { data1: {
label: 'CEO', key: '0',
type: 'person', type: 'person',
className: 'p-person', styleClass: 'p-person',
expanded: true, data: {label: 'CEO', name: 'Walter White', avatar: 'walter.jpg'},
data: {name:'Walter White', 'avatar': 'walter.jpg'},
children: [ children: [
{ {
label: 'CFO', key: '0_0',
type: 'person', type: 'person',
className: 'p-person', styleClass: 'p-person',
expanded: true, data: {label: 'CFO', name:'Saul Goodman', avatar: 'saul.jpg'},
data: {name:'Saul Goodman', 'avatar': 'saul.jpg'},
children:[{ children:[{
label: 'Tax', key: '0_0_0',
className: 'department-cfo' data: {label: 'Tax'},
styleClass: 'department-cfo'
}, },
{ {
label: 'Legal', key: '0_0_1',
className: 'department-cfo' data: {label: 'Legal'},
styleClass: 'department-cfo'
}], }],
}, },
{ {
label: 'COO', key: '0_1',
type: 'person', type: 'person',
className: 'p-person', styleClass: 'p-person',
expanded: true, data: {label: 'COO', name:'Mike E.', avatar: 'mike.jpg'},
data: {name:'Mike E.', 'avatar': 'mike.jpg'},
children:[{ children:[{
label: 'Operations', key: '0_1_0',
className: 'department-coo' data: {label: 'Operations'},
styleClass: 'department-coo'
}] }]
}, },
{ {
label: 'CTO', key: '0_2',
type: 'person', type: 'person',
className: 'p-person', styleClass: 'p-person',
expanded: true, data: {label: 'CTO', name:'Jesse Pinkman', avatar: 'jesse.jpg'},
data: {name:'Jesse Pinkman', 'avatar': 'jesse.jpg'},
children:[{ children:[{
label: 'Development', key: '0_2_0',
className: 'department-cto', data: {label: 'Development'},
expanded: true, styleClass: 'department-cto',
children:[{ children:[{
label: 'Analysis', key: '0_2_0_0',
className: 'department-cto' data: {label: 'Analysis'},
styleClass: 'department-cto'
}, },
{ {
label: 'Front End', key: '0_2_0_1',
className: 'department-cto' data: {label: 'Front End'},
styleClass: 'department-cto'
}, },
{ {
label: 'Back End', key: '0_2_0_2',
className: 'department-cto' data: {label: 'Back End'},
styleClass: 'department-cto'
}] }]
}, },
{ {
label: 'QA', key: '0_2_1',
className: 'department-cto' data: {label: 'QA'},
styleClass: 'department-cto'
}, },
{ {
label: 'R&D', key: '0_2_2',
className: 'department-cto' data: {label: 'R&D'},
styleClass: 'department-cto'
}] }]
} }
] ]
}, },
data2 : [{ data2 : {
label: 'F.C Barcelona', key: '0',
expanded: true, data: {label: 'F.C. Barcelona'},
children: [ children: [
{ {
label: 'F.C Barcelona', key: '0_0',
expanded: true, data: {label: 'F.C. Barcelona'},
children: [ children: [
{ {
label: 'Chelsea FC' key: '0_0_0',
data: {label: 'Chelsea F.C.'}
}, },
{ {
label: 'F.C. Barcelona' key: '0_0_1',
data: {label: 'F.C. Barcelona'}
} }
] ]
}, },
{ {
label: 'Real Madrid', key: '0_1',
expanded: true, data: {label: 'Real Madrid'},
children: [ children: [
{ {
label: 'Bayern Munich' key: '0_1_0',
data: {label: 'Bayern Munich'}
}, },
{ {
label: 'Real Madrid' key: '0_1_1',
data: {label: 'Real Madrid'}
} }
] ]
} }
] ]
}], },
selection: [] selection: []
} }
}, },
@ -126,3 +154,59 @@ export default {
} }
} }
</script> </script>
<style scoped lang="scss">
/deep/ .p-organizationchart {
.p-person {
padding: 0;
border: 0 none;
}
.node-header, .node-content {
padding: .5em .7em;
}
.node-header {
background-color: #495ebb;
color: #ffffff;
}
.node-content {
text-align: center;
border: 1px solid #495ebb;
}
.node-content img {
border-radius: 50%;
}
.department-cfo {
background-color: #7247bc;
color: #ffffff;
}
.department-coo {
background-color: #a534b6;
color: #ffffff;
}
.department-cto {
background-color: #e9286f;
color: #ffffff;
}
.p-organizationchart .p-highlight {
background-color: orange;
}
.p-person .p-node-toggler {
color: #495ebb !important;
}
.department-cto .p-node-toggler {
color: #8a0a39 !important;
}
}
</style>