主要用于展示公司组织架构各个节点图
传递对应的配置数据即可
基础默认样式,可以自行配置颜色跟展示效果
index.vue
<template>
<div>
<div class="drag-container">
<div class="drag-div">
<!-- <div class="drag-div" v-candrag> -->
<div class="tree_container">
<div>
<div
class="tree_title"
:style="{ color: titleColor, fontSize: titleFontSize }"
>
<span>{
{ title }}</span>
</div>
<template>
<div class="tree_slot">
<slot name="head"></slot>
</div>
</template>
</div>
</div>
<!-- 顶级节点结构 对象结构 -->
<div
v-if="!Array.isArray(treeData) && Object.keys(treeData).length > 0"
>
<div
class="tree_topLevel_lable"
:style="{ '--dynamic-color': lineColor }"
>
<div
class="tree_topLevel"
:class="{
isRound_item: isRound
}"
:style="{
borderColor: borderColor,
background: topNodeColor
}"
>
<div>
{
{ treeData.name }}
</div>
<!-- 展开 -->
<div
style="display: flex;width: 100%;justify-content: space-around;"
>
<div
class="tree_icon"
:style="{ color: lineColor }"
@click.stop="treeCLick(treeData)"
v-show="treeData.children && treeData.children.length > 0"
>
<i
class="el-icon-circle-plus-outline"
v-show="!treeData.checked"
></i>
<i
class="el-icon-remove-outline"
v-show="treeData.checked"
></i>
</div>
</div>
</div>
</div>
<!-- 常规节点 -->
<!-- 组织 -->
<div class="tree_organization" v-show="treeData.checked">
<!-- 对象 -->
<div v-if="treeData && isObject(treeData) && treeData.children">
<tree
:data="treeData.children"
:cutSubscript="cutSubscript"
:canItBeExpanded="canItBeExpanded"
@treeItemClick="treeItemClick"
:isRound="isRound"
:parentNodeBgColor="parentNodeBgColor"
:parentNodeTitleColor="parentNodeTitleColor"
:childNodeBgColor="childNodeBgColor"
:childNodeTitleColor="childNodeTitleColor"
:lineColor="lineColor"
:borderColor="borderColor"
:isHorizontal="isHorizontal"
></tree>
</div>
</div>
</div>
<div v-else>
<!-- 数组 -->
<div v-if="treeData && Array.isArray(treeData)">
<tree
:data="treeData"
:cutSubscript="cutSubscript"
@treeItemClick="treeItemClick"
:canItBeExpanded="canItBeExpanded"
:isRound="isRound"
:parentNodeBgColor="parentNodeBgColor"
:parentNodeTitleColor="parentNodeTitleColor"
:childNodeBgColor="childNodeBgColor"
:childNodeTitleColor="childNodeTitleColor"
:lineColor="lineColor"
:borderColor="borderColor"
:isHorizontal="isHorizontal"
></tree>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import tree from "./tree.vue";
export default {
components: {
tree
},
props: {
// 标题
title: {
type: String,
default: ""
},
// 标题颜色
titleColor: {
type: String,
default: "#151517"
},
// 标题字体大小
titleFontSize: {
type: String,
default: "26px"
},
// 标题字体粗细
titleFontWeight: {
type: String,
default: "bold"
},
// 是否父节点横向展示
isHorizontal: {
type: Boolean,
default: false
},
// 是否圆角
isRound: {
type: Boolean,
default: false
},
// 顶级节点颜色
topNodeColor: {
type: String,
default: "#0094de"
},
// 顶级节点标题颜色
topNodeTitleColor: {
type: String,
default: "#fff"
},
// 父节点背景颜色
parentNodeBgColor: {
type: String,
default: "#0094de"
},
// 父节点标题颜色
parentNodeTitleColor: {
type: String,
default: "#fff"
},
// 子节点背景颜色
childNodeBgColor: {
type: String,
default: "#0094de"
},
// 子节点标题颜色
childNodeTitleColor: {
type: String,
default: "#fff"
},
// 连接线颜色
lineColor: {
type: String,
default: "#151517"
},
// 边框颜色
borderColor: {
type: String,
default: "#151517"
},
// 是否可以点击+号展开
canItBeExpanded: {
type: Boolean,
default: true
},
title: {
type: String,
default: ""
},
treeData: {
type: [Array, Object],
// Array 没有顶级节点
// Object 有顶级节点
default: () => {
return [];
}
},
cutSubscript: {
type: Number,
default: 11
},
allopen: {
type: Boolean,
default: false
}
},
data() {
return {};
},
computed: {
userInfo: {
get() {
return this.store.getters.userInfo;
}
}
},
watch: {},
created() {},
mounted() {},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
//递归展开
openAll(data) {
if (data && data.length > 0) {
data.forEach(item => {
this.$set(item, "checked", true);
if (item.children && item.children.length > 0) {
this.openAll(item.children);
}
});
}
},
openAllObj(data) {
if (data && data.children && data.children.length > 0) {
this.$set(data, "checked", true);
data.children.forEach(item => {
this.$set(item, "checked", true);
if (item.children && item.children.length > 0) {
this.openAll(item.children);
}
});
}
},
treeItemClick(item) {
this.$emit("treeItemClick", item);
},
treeCLick(item) {
this.$nextTick(() => {
if (item.children && item.children.length > 0) {
if (!this.canItBeExpanded) return;
this.$set(item, "checked", !item.checked);
}
});
},
isObject(value) {
return (
value !== null && typeof value === "object" && !(value instanceof Array)
);
}
}
};
</script>
<style scoped lang="scss">
.tree_container {
padding: 20px;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
}
.tree_title {
font-size: 26px;
font-weight: bold;
font-family: fangsong;
}
.tree_topLevel {
border: 1px solid black;
border-radius: 4px;
min-width: 75px;
background-color: #fff;
padding: 12px 16px;
position: relative;
cursor: pointer;
text-align: center;
background-color: #0094de;
color: #fff;
font-size: 16px;
font-weight: 600;
}
.tree_topLevel_lable {
display: flex;
margin-top: 30px;
justify-content: center;
position: relative;
}
.tree_topLevel_lable::after {
position: absolute;
content: "";
display: block;
width: 1px;
height: 30px;
bottom: -30px;
border-left: 1px solid var(--dynamic-color);
}
.tree_organization {
display: flex;
justify-content: center;
margin-top: 10px;
}
.tree_icon {
padding: 4px;
z-index: 3;
position: absolute;
bottom: -42px;
background-color: #fff;
font-size: 20px;
color: #000;
// left: 44.3%;
}
.drag-container {
width: 100%;
height: 100%;
position: relative;
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard syntax */
}
// .drag-div {
// position: absolute; /* 把拖拽元素设置为绝对定位,非常重要!!! */
// width: 300%;
// height: 300%;
// }
.draggable_str {
cursor: grab;
}
.isRound_item {
border-radius: 25px !important;
}
</style>
tree.vue
<template>
<div class="org-chart">
<div
v-for="(item, index) in data"
:key="index"
class="org-item"
:title="item.name"
>
<div
class="org-item-content"
:class="{
'org-first-item': index == 0 && data.length != 1,
'org-lase-item': index == data.length - 1 && data.length != 1,
'org-single-item': data.length == 1
}"
:style="{ '--dynamic-color': lineColor }"
>
<div
class="org-link"
:class="{
link_children:((item.children && item.children.length > 0) || item.isChild) && isHorizontal
}"
:style="{ '--dynamic-color': lineColor }"
>
<div
class="org_name"
:class="{
'org-is-child':((item.children && item.children.length > 0) || item.isChild) && isHorizontal,
isRound_item: isRound
}"
:style="{
borderColor: borderColor,
'--dynamic-bgcolor': parentNodeBgColor,
'--dynamic-bgTitlecolor': parentNodeTitleColor,
'--dynamic-childColor': childNodeBgColor,
'--dynamic-childTitleColor': childNodeTitleColor
// 边框阴影
// boxShadow: `1px 0 0px ${borderColor}`
}"
>
<div class="org_name_flex" @click.stop="orgItemClick(item, data)">
<div
v-for="(text, keys) in cutString(item.name)"
:key="keys"
:class="{
or_childFrom:((item.children && item.children.length > 0) || item.isChild) && isHorizontal
}"
>
<div
class="org-title"
v-for="(character, textkey) in text"
:key="textkey"
:class="{
fh_transform:
text[textkey] == '(' ||
text[textkey] == ')' ||
text[textkey] == '(' ||
text[textkey] == ')',
sz_transform: isNumberString(text[textkey]),
jg_transform: item.children && item.children.length > 0
}"
>
{
{ text[textkey] }}
</div>
</div>
</div>
<div
class="org-icon"
:style="{ color: lineColor }"
:class="{
icon_childFrom: item.children && item.children.length > 0 && isHorizontal
}"
@click.stop="orgCLick(item)"
v-show="item.children && item.children.length > 0"
>
<i class="el-icon-circle-plus-outline" v-show="!item.checked"></i>
<i class="el-icon-remove-outline" v-show="item.checked"></i>
</div>
</div>
</div>
</div>
<div
v-if="item.children && item.children.length > 0 && item.checked"
class="org-children"
>
<org-chart
:data="item.children"
@orgItemClick="orgItemClick"
:canItBeExpanded="canItBeExpanded"
:isRound="isRound"
:parentNodeBgColor="parentNodeBgColor"
:parentNodeTitleColor="parentNodeTitleColor"
:childNodeBgColor="childNodeBgColor"
:childNodeTitleColor="childNodeTitleColor"
:lineColor="lineColor"
:borderColor="borderColor"
:cutSubscript="cutSubscript"
:isHorizontal="isHorizontal"
></org-chart>
</div>
</div>
</div>
</template>
<script>
export default {
name: "OrgChart",
components: {},
props: {
// 是否父节点横向展示
isHorizontal: {
type: Boolean,
default: false
},
// 是否可以点击+号展开
canItBeExpanded: {
type: Boolean,
default: true
},
data: {
type: Array,
required: true
},
// 是否圆角
isRound: {
type: Boolean,
default: false
},
// 父节点背景颜色
parentNodeBgColor: {
type: String,
default: "#0094de"
},
// 父节点标题颜色
parentNodeTitleColor: {
type: String,
default: "#fff"
},
// 子节点背景颜色
childNodeBgColor: {
type: String,
default: "#0094de"
},
// 子节点标题颜色
childNodeTitleColor: {
type: String,
default: "#fff"
},
// 连接线颜色
lineColor: {
type: String,
default: "#151517"
},
// 边框颜色
borderColor: {
type: String,
default: "#151517"
},
// 字符切割
cutSubscript: {
type: Number,
default: 11
}
},
data() {
return {};
},
computed: {
userInfo: {
get() {
return this.store.getters.userInfo;
}
}
},
watch: {},
created() {},
mounted() {},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 字符切割
cutString(str) {
if (!str) return [];
let num = this.cutSubscript;
if (str.length <= num) return [str];
if (str.length % 2 == 0) {
num = str.length / 2;
} else {
num = Math.floor(str.length / 2) + 1;
}
let result = [];
for (let i = 0; i < str.length; i += num) {
result.push(str.slice(i, i + num));
}
return result;
},
// 判断是否数字
isNumberString(str) {
const num = Number(str);
return !isNaN(num) && str == num;
},
orgCLick(item) {
this.$nextTick(() => {
if (item.children && item.children.length > 0) {
if (!this.canItBeExpanded) return;
this.$set(item, "checked", !item.checked);
console.log(123, "???");
}
});
},
orgItemClick(item, data) {
this.$emit("treeItemClick", item, data);
}
}
};
</script>
<style scoped lang="scss">
.org-chart {
display: flex;
justify-content: center;
margin-top: 25px;
padding: 20px 0;
}
.org-item {
// padding: 0 5px;
position: relative;
}
.org_name {
cursor: pointer;
position: relative;
// text-align: center;
padding: 8px;
border: 1px solid black;
border-radius: 4px;
// writing-mode: vertical-lr;
width: 53px;
height: 240px;
background-color: var(--dynamic-childColor);
// display: flex;
// align-items: center;
}
.org_name_flex {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
letter-spacing: 1px;
}
.org-title {
text-align: center;
margin: auto;
color: var(--dynamic-childTitleColor);
font-size: 16px;
font-weight: 600;
}
.org-icon {
z-index: 1;
position: absolute;
bottom: -32px;
background-color: #fff;
font-size: 20px;
left: 12px;
padding: 4px;
}
.icon_childFrom {
left: 43.5% !important;
}
.org-item-content {
padding: 0 5px;
display: flex;
justify-content: center;
}
.org-link {
z-index: 1;
position: relative;
}
.org-chart {
position: relative;
}
.org-item-content::before {
position: absolute;
content: "";
display: block;
width: 100%;
height: 0px;
border-top: 1px solid var(--dynamic-color);
top: -20px;
left: 0;
}
.org-link::before {
position: absolute;
content: "";
display: block;
width: 1px;
height: 8%;
border-left: 1px solid var(--dynamic-color);
top: -8%;
left: 50%;
}
.link_children::before {
top: -46% !important;
height: 46% !important;
}
.org-is-child {
width: 220px !important;
height: 43px !important;
background-color: var(--dynamic-bgcolor) !important;
.org-title {
color: var(--dynamic-bgTitlecolor) !important;
}
}
.org-first-item::before {
width: 50% !important;
left: 50% !important;
}
.org-lase-item::before {
width: 50% !important;
}
.org-single-item::before {
width: 0% !important;
left: 50% !important;
top: -10% !important;
// border-top: 10px solid #151517 !important;
}
.fh_transform {
transform: rotate(90deg);
}
.sz_transform {
transform: rotate(90deg);
}
.jg_transform {
transform: rotate(0deg) !important;
}
.or_childFrom {
display: flex;
align-items: center;
}
.isRound_item {
border-radius: 25px !important;
}
</style>
使用方法
html部分
事件按需求自行绑定
<treeDiagram :treeData="orgData"/>
js部分
import treeDiagram from "@/components/treeDiagram/index";
export default {
components: {
treeDiagram
},
data(){
return{
orgData: [
{
name: "综合办公司",
children: [
{ name: "党委巡察办公室" },
{
name: "党委巡察办公室2",
children: [
{ name: "科技和信息化部1" },
{ name: "科技和信息化部2" },
{
name: "科技和信息化部3",
children: [
{
name: "科技和信息化部1",
children: [{ name: "科技和信息化部3" }]
},
{ name: "科技和信息化部2" },
{ name: "科技和信息化部3" }
]
}
]
},
{ name: "党委巡察办公室2" },
{ name: "党委巡察办公室2" }
]
},
{
name: "党建工作部1",
children: [
{ name: "科技和信息化部1" },
{ name: "科技和信息化部2" },
{ name: "科技和信息化部3" }
]
},
{
name: "党建工作部2",
children: [{ name: "运营事业部1" }, { name: "运营事业部2" }]
}
],
}
},
methods:{
}
}