实现组织架构图(vue-org-tree)
如果向使用原来的依赖可以使用这个人的,因为我也是根据这个博客大佬仿照Vue-org-tree实现的方案
对此有几点不惑,问了大佬,大佬也没有回复我

由于还在启动项目中,还没有完善,目前已经可以支持动态创建节点了

这是通过点击实现抽屉展示

这是通过hover鼠标悬停高亮,由于我并不是一个正儿八经的前端,准确的说我是来搞笑的。所以对于每个节点面板渲染是很简陋的。
# 这里是安装大佬提供的依赖
npm install vue-tree-color
检查less和less-loader,因为该组件使用到了less,可以看看项目工程是否已经安装
npm install --save-dev less less-loader
注意这里可能会在项目启动的时候出现以下问题
报错信息:Syntax Error: TypeError: this.getOptions is not a function
解决方案-需要在安装less和lessloader时,指定版本号,否则就会导致兼容问题。
npm i less@3.9.0 less-loader@4.1.0 -D
找到main.js中追加一下内容
import Vue2OrgTree from 'vue-tree-color'
Vue.use(Vue2OrgTree)
在公共层面@components 目录下,我们需要进行二次封装,为什么要二次封装,上面已经说到原因了,因为定制化内容不同, 肯定会涉及更改组件渲染方式以及样式,因为二次封装是非常有必要的

我就随便给取了一个名字 SimpleTree 目录
组件页面,我没做更改,直接拿来用
$emit('on-expand', e, data)"@on-node-focus="(e, data) => $emit('on-node-focus', e, data)"@on-node-click="(e, data) => $emit('on-node-click', e, data)"@on-node-mouseover="(e, data) => $emit('on-node-mouseover', e, data)"@on-node-mouseout="(e, data) => $emit('on-node-mouseout', e, data)"/>
这个js就是组件渲染的函数,可以稍微研究一下
// 判断是否叶子节点
const isLeaf = (data, prop) => {return !(Array.isArray(data[prop]) && data[prop].length > 0)
}
// 创建 node 节点
export const renderNode = (h, data, context) => {const { props } = contextconst cls = ['org-tree-node']const childNodes = []const children = data[props.props.children]if (isLeaf(data, props.props.children)) {cls.push('is-leaf')} else if (props.collapsable && !data[props.props.expand]) {cls.push('collapsed')}if (data) {childNodes.push(renderLabel(h, data, context))}if ((!props.collapsable || data[props.props.expand]) && children.length > 0) {childNodes.push(renderChildren(h, children, context))}return h('div', {domProps: {className: cls.join(' ')}}, childNodes)
}// 创建展开折叠按钮
export const renderBtn = (h, data, { props, listeners }) => {const expandHandler = listeners['on-expand']let cls = ['org-tree-node-btn']if (data[props.props.expand]) {cls.push('expanded')}return h('span', {domProps: {className: cls.join(' ')},on: {click: e => expandHandler && expandHandler(e,data)}})
}// 创建 label 节点
export const renderLabel = (h, data, context) => {const { props, listeners } = contextconst label = data[props.props.label]const renderContent = props.renderContent// event handlersconst clickHandler = listeners['on-node-click']const mouseOverHandler = listeners['on-node-mouseover']const mouseOutHandler = listeners['on-node-mouseout']const childNodes = []if (typeof renderContent === 'function') {let vnode = renderContent(h, data)vnode && childNodes.push(vnode)} else {childNodes.push(label)}if (props.collapsable && !isLeaf(data, props.props.children)) {childNodes.push(renderBtn(h, data, context))}const cls = ['org-tree-node-label-inner']let { labelWidth, labelClassName, selectedClassName, selectedKey ,judge,NodeClass} = propsif (typeof labelWidth === 'number') {labelWidth += 'px'}if (typeof labelClassName === 'function') {labelClassName = labelClassName(data)}labelClassName && cls.push(labelClassName)// add selected class and key from propsif (typeof selectedClassName === 'function') {selectedClassName = selectedClassName(data)}selectedClassName && selectedKey && data[selectedKey] && cls.push(selectedClassName)return h('div', {domProps: {className: 'org-tree-node-label'}}, [h('div', {domProps: {className:ChangeTheColor(data,judge,NodeClass) + " org-tree-node-label-inner"},style: { width: labelWidth },on: {'click': e => clickHandler && clickHandler(e, data),'mouseover': e => mouseOverHandler && mouseOverHandler(e, data),'mouseout': e => mouseOutHandler && mouseOutHandler(e, data)}}, childNodes)])
}function ChangeTheColor(e,judge,NodeClass){if(judge !== "" && judge !== undefined && judge !== null && judge.swtich !== false){for(var k in judge) {var a = (eval("e."+k))if(NodeClass){for(let c =0 ;cif( a === NodeClass[c])return NodeClass[c]else if(NodeClass.length-1==c)return ""}}else{return ""}}}else{return ""}
}
// 创建 node 子节点
export const renderChildren = (h, list, context) => {if (Array.isArray(list) && list.length > 0) {const children = list.map(item => {return renderNode(h, item, context)})return h('div', {domProps: {className: 'org-tree-node-children'}}, children)}return ''
}export const render = (h, context) => {const {props} = contextif (props.data.id) {return renderNode(h, props.data, context)} else {return ''}}export default render
.org-tree-container {display: inline-block;padding: 15px;background-color: #fff;
}.org-tree {// display: inline-block;display: table;text-align: center;&:before, &:after {content: '';display: table;}&:after {clear: both;}
}.org-tree-node,
.org-tree-node-children {position: relative;margin: 0;padding: 0;list-style-type: none;&:before, &:after {transition: all .35s;}
}
.org-tree-node-label {position: relative;display: inline-block;.org-tree-node-label-inner {padding: 10px 15px;text-align: center;border-radius: 5px;box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);-webkit-transition-duration: 0.3s;transition-duration: 0.3s;cursor: pointer;}.org-tree-node-label-inner:hover {background-color: #c6e2ff;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)}}.render-node-panel-header {display: flex;flex-direction: row;flex-wrap: nowrap;justify-content: center;align-items: center;margin: 10px 0;
}
.node-box {min-width: 200px;
}.render-node-panel-middle {display: flex;flex-direction: row;justify-content: space-around;align-items: center;
}.org-tree-node-btn {position: absolute;top: 100%;left: 50%;width: 20px;height: 20px;z-index: 10;margin-left: -11px;margin-top: 9px;background-color: #fff;border: 1px solid #ccc;border-radius: 50%;box-shadow: 0 0 2px rgba(0, 0, 0, .15);cursor: pointer;transition: all .35s ease;&:hover {background-color: #e7e8e9;transform: scale(1.15);}&:before, &:after {content: '';position: absolute;}&:before {top: 50%;left: 4px;right: 4px;height: 0;border-top: 1px solid #ccc;}&:after {top: 4px;left: 50%;bottom: 4px;width: 0;border-left: 1px solid #ccc;}&.expanded:after {border: none;}
}
.org-tree-node {padding-top: 20px;display: table-cell;vertical-align: top;&.is-leaf, &.collapsed {padding-left: 10px;padding-right: 10px;}&:before, &:after {content: '';position: absolute;top: 0;left: 0;width: 50%;height: 19px;}&:after {left: 50%;border-left: 1px solid #ddd;}&:not(:first-child):before,&:not(:last-child):after {border-top: 1px solid #ddd;}}
.collapsable .org-tree-node.collapsed {padding-bottom: 30px;.org-tree-node-label:after {content: '';position: absolute;top: 100%;left: 0;width: 50%;height: 20px;border-right: 1px solid #ddd;}
}
.org-tree > .org-tree-node {padding-top: 0;&:after {border-left: 0;}
}
.org-tree-node-children {padding-top: 20px;display: table;&:before {content: '';position: absolute;top: 0;left: 50%;width: 0;height: 20px;border-left: 1px solid #ddd;}&:after {content: '';display: table;clear: both;}
}.horizontal {.org-tree-node {// display: flex;// flex-direction: row;// justify-content: flex-start;// align-items: center;display: table-cell;float: none;padding-top: 0;padding-left: 20px;&.is-leaf, &.collapsed {padding-top: 10px;padding-bottom: 10px;}&:before, &:after {width: 19px;height: 50%;}&:after {top: 50%;left: 0;border-left: 0;}&:only-child:before {top: 1px;border-bottom: 1px solid #ddd;}&:not(:first-child):before,&:not(:last-child):after {border-top: 0;border-left: 1px solid #ddd;}&:not(:only-child):after {border-top: 1px solid #ddd;}.org-tree-node-inner {display: table;}}.org-tree-node-label {display: table-cell;vertical-align: middle;}&.collapsable .org-tree-node.collapsed {padding-right: 30px;.org-tree-node-label:after {top: 0;left: 100%;width: 20px;height: 50%;border-right: 0;border-bottom: 1px solid #ddd;}}.org-tree-node-btn {top: 50%;left: 100%;margin-top: -11px;margin-left: 9px;}& > .org-tree-node:only-child:before {border-bottom: 0;}.org-tree-node-children {// display: flex;// flex-direction: column;// justify-content: center;// align-items: flex-start;display: table-cell;padding-top: 0;padding-left: 20px;&:before {top: 50%;left: 0;width: 20px;height: 0;border-left: 0;border-top: 1px solid #ddd;}&:after {display: none;}& > .org-tree-node {display: block;}}
}
data: 就是树形结构,下面会有案例
horizontal:排列形式
collapsable: 是否展开
on-expand: 展开、闭合节点
on-node-click: 节点点击事件
on-node-mouseover:鼠标悬停
on-node-mouseout:鼠标悬出
renderContent:渲染函数
data(){return{labelClassName: "bg-color-orange", // 看到node.js 中这个实际没用的basicInfo: { id: null, label: null },basicSwitch: false,data: {id: 0,label: "XXX科技有限公司",className: 'nxnnxnxn', // 切记HTML的class 标签属性不能以数字开头children: [{id: 2,label: "产品研发部",className: 'nxnnxnxn-1',children: [{id: 5,label: "研发-前端",children: [{id: 55,className: 'nxnnxnxn-55',label: "前端1"},{id: 56,className: 'nxnnxnxn-56',label: "前端2"},{id: 57,className: 'nxnnxnxn-57',label: "前端3"},{id: 58,className: 'nxnnxnxn-58',label: "前端4"}]}]}]},}
},
methods:{//渲染节点renderContent(h, data) {// 通过data中的className属性来对div元素进行注入class// 每个节点渲染必然会走这个函数//这里对应的不同的className 需要在上面的tree.less中写入样式return (data.className}>{data.label}男测试人员);},//鼠标移出onMouseout(e, data) {console.log("onMouseout", data)},//鼠标移入onMouseover(e, data) {console.log("onMouseover", data)},//点击节点NodeClick(e, data) {console.log(e, data);},//默认展开toggleExpand(data, val) {if (Array.isArray(data)) {data.forEach(item => {this.$set(item, "expand", val);if (item.children) {this.toggleExpand(item.children, val);}});} else {this.$set(data, "expand", val);if (data.children) {this.toggleExpand(data.children, val);}}},collapse(list) {list.forEach(child => {if (child.expand) {child.expand = false;}child.children && this.collapse(child.children);});},//展开onExpand(e, data) {if ("expand" in data) {data.expand = !data.expand;if (!data.expand && data.children) {this.collapse(data.children);}} else {this.$set(data, "expand", true);}},
}