一、说明
在iview-admin2.4版本之前是没有集成v-org-tree的,那在2.4之前的版本想引入v-org-tree要怎么做呢?下面一步一步来记录下使用v-org-tree插件的过程
二、效果图
三、开始记录过程
1、安装v-org-tree插件,在项目的目录下执行
npm install v-org-tree
2、在main.js中引入v-org-tree ,这一步很重要
import { directive as clickOutside } from 'v-click-outside-x'
import OrgTree from 'v-org-tree'
import 'v-org-tree/dist/v-org-tree.css'
Vue.use(OrgTree)
# 下面这个在注册完Vue指令后面
Vue.directive('clickOutside', clickOutside)
3、在view下面新建一个org-tree文件夹
新建一个index.vue,内容如下
<template>
<Card shadow style="height: 100%;width: 100%;overflow:hidden">
<div class="department-outer">
<div class="zoom-box">
<zoom-controller v-model="zoom" :min="20" :max="200"></zoom-controller>
</div>
<div class="view-box">
<org-view
v-if="data"
:data="data"
:zoom-handled="zoomHandled"
@on-menu-click="handleMenuClick"
></org-view>
</div>
</div>
</Card>
</template>
<script>
import OrgView from './components/org-view.vue'
import ZoomController from './components/zoom-controller.vue'
// import { getOrgData } from '@/api/data'
import './index.less'
const menuDic = {
edit: '编辑部门',
detail: '查看部门',
new: '新增子部门',
delete: '删除部门'
}
export default {
name: 'org_tree_page',
components: {
OrgView,
ZoomController
},
data() {
return {
data: {
id: 0,
label: 'XXX科技有限公司',
children: [
{
id: 2,
label: '产品研发部',
children: [
{
id: 5,
label: '研发-前端'
},
{
id: 6,
label: '研发-后端'
},
{
id: 9,
label: 'UI设计'
},
{
id: 10,
label: '产品经理'
}
]
},
{
id: 3,
label: '销售部',
children: [
{
id: 7,
label: '销售一部'
},
{
id: 8,
label: '销售二部'
}
]
},
{
id: 4,
label: '财务部'
},
{
id: 11,
label: 'HR人事'
}
]
},
zoom: 100
}
},
computed: {
zoomHandled() {
return this.zoom / 100
}
},
methods: {
setDepartmentData(data) {
data.isRoot = true
return data
},
handleMenuClick({ data, key }) {
console.log(data, key)
this.$Message.success({
duration: 5,
content: `点击了《${data.label}》节点的'${menuDic[key]}'菜单`
})
},
getDepartmentData() {
// getOrgData().then(res => {
// const { data } = res
// this.data = data
// })
}
},
mounted() {
this.getDepartmentData()
}
}
</script>
<style>
</style>
新建一个index.less文件
@wrapper: ~'department';
.percent-100 {
width: 100%;
height: 100%;
}
.@{wrapper}-outer {
.percent-100;
overflow: hidden;
.tip-box{
position: absolute;
left: 20px;
top: 20px;
z-index: 12;
}
.zoom-box {
position: absolute;
right: 30px;
bottom: 30px;
z-index: 2;
}
.view-box {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
cursor: move;
.org-tree-drag-wrapper {
width: 100%;
height: 100%;
}
.org-tree-wrapper {
display: inline-block;
position: absolute;
left: 50%;
top: 50%;
transition: transform 0.2s ease-out;
.org-tree-node-label {
box-shadow: 0px 2px 12px 0px rgba(143, 154, 165, 0.4);
border-radius: 4px;
.org-tree-node-label-inner {
padding: 0;
.custom-org-node {
padding: 14px 41px;
background: #738699;
user-select: none;
word-wrap: none;
white-space: nowrap;
border-radius: 4px;
color: #ffffff;
font-size: 14px;
font-weight: 500;
line-height: 20px;
transition: background 0.1s ease-in;
cursor: default;
&:hover {
background: #5d6c7b;
transition: background 0.1s ease-in;
}
&.has-children-label {
cursor: pointer;
}
.context-menu{
position: absolute;
right: -10px;
bottom: 20px;
z-index: 10;
}
}
}
}
}
}
}
在org-tree目录下新建一个components文件夹,然后在该文件夹下面新建org-view.vue
<template>
<div
ref="dragWrapper"
class="org-tree-drag-wrapper"
@mousedown="mousedownView"
@contextmenu="handleDocumentContextmenu"
>
<div class="org-tree-wrapper" :style="orgTreeStyle">
<v-org-tree
v-if="data"
:data="data"
:node-render="nodeRender"
:expand-all="true"
@on-node-click="handleNodeClick"
collapsable
></v-org-tree>
</div>
</div>
</template>
<script>
import { on, off } from '@/libs/tools'
const menuList = [
{
key: 'edit',
label: '编辑部门'
},
{
key: 'detail',
label: '查看部门'
},
{
key: 'new',
label: '新增子部门'
},
{
key: 'delete',
label: '删除部门'
}
]
export default {
name: 'OrgView',
props: {
zoomHandled: {
type: Number,
default: 1
},
data: Object
},
data () {
return {
currentContextMenuId: '',
orgTreeOffsetLeft: 0,
orgTreeOffsetTop: 0,
initPageX: 0,
initPageY: 0,
oldMarginLeft: 0,
oldMarginTop: 0,
canMove: false
}
},
computed: {
orgTreeStyle () {
return {
transform: `translate(-50%, -50%) scale(${this.zoomHandled}, ${
this.zoomHandled
})`,
marginLeft: `${this.orgTreeOffsetLeft}px`,
marginTop: `${this.orgTreeOffsetTop}px`
}
}
},
methods: {
handleNodeClick (e, data, expand) {
expand()
},
closeMenu () {
this.currentContextMenuId = ''
},
getBgColor (data) {
return this.currentContextMenuId === data.id
? data.isRoot
? '#0d7fe8'
: '#5d6c7b'
: ''
},
nodeRender (h, data) {
return (
<div
class={[
'custom-org-node',
data.children && data.children.length ? 'has-children-label' : ''
]}
on-mousedown={event => event.stopPropagation()}
on-contextmenu={this.contextmenu.bind(this, data)}
>
{data.label}
<dropdown
trigger="custom"
class="context-menu"
visible={this.currentContextMenuId === data.id}
nativeOn-click={this.handleDropdownClick}
on-on-click={this.handleContextMenuClick.bind(this, data)}
style={{
transform: `scale(${1 / this.zoomHandled}, ${1 /
this.zoomHandled})`
}}
v-click-outside={this.closeMenu}
>
<dropdown-menu slot="list">
{menuList.map(item => {
return (
<dropdown-item name={item.key}>{item.label}</dropdown-item>
)
})}
</dropdown-menu>
</dropdown>
</div>
)
},
contextmenu (data, $event) {
let event = $event || window.event
event.preventDefault
? event.preventDefault()
: (event.returnValue = false)
this.currentContextMenuId = data.id
},
setDepartmentData (data) {
data.isRoot = true
this.departmentData = data
},
mousedownView (event) {
this.canMove = true
this.initPageX = event.pageX
this.initPageY = event.pageY
this.oldMarginLeft = this.orgTreeOffsetLeft
this.oldMarginTop = this.orgTreeOffsetTop
on(document, 'mousemove', this.mousemoveView)
on(document, 'mouseup', this.mouseupView)
},
mousemoveView (event) {
if (!this.canMove) return
const { pageX, pageY } = event
this.orgTreeOffsetLeft = this.oldMarginLeft + pageX - this.initPageX
this.orgTreeOffsetTop = this.oldMarginTop + pageY - this.initPageY
},
mouseupView () {
this.canMove = false
off(document, 'mousemove', this.mousemoveView)
off(document, 'mouseup', this.mouseupView)
},
handleDropdownClick (event) {
event.stopPropagation()
},
handleDocumentContextmenu () {
this.canMove = false
},
handleContextMenuClick (data, key) {
this.$emit('on-menu-click', { data, key })
}
},
mounted () {
on(document, 'mousedown', this.mousedownView)
on(document, 'contextmenu', this.handleDocumentContextmenu)
},
beforeDestroy () {
off(document, 'mousedown', this.mousedownView)
off(document, 'contextmenu', this.handleDocumentContextmenu)
}
}
</script>
<style>
</style>
在同级目录下新建zoom-controller.vue文件
<template>
<div class="zoom-wrapper">
<button class="zoom-button" @click="scale('down')">
<Icon type="md-remove" :size="14" color="#fff"/>
</button>
<span class="zoom-number">{{ value }}%</span>
<button class="zoom-button" @click="scale('up')">
<Icon type="md-add" :size="14" color="#fff"/>
</button>
</div>
</template>
<script>
export default {
name: 'ZoomController',
props: {
value: {
type: Number,
default: 100
},
step: {
type: Number,
default: 20
},
min: {
type: Number,
default: 10
},
max: {
type: Number,
default: 200
}
},
methods: {
scale (type) {
const zoom = this.value + (type === 'down' ? -this.step : this.step)
if (
(zoom < this.min && type === 'down') ||
(zoom > this.max && type === 'up')
) {
return
}
this.$emit('input', zoom)
}
}
}
</script>
<style lang="less">
.trans(@duration) {
transition: ~"all @{duration} ease-in";
}
.zoom-wrapper {
.zoom-button {
width: 20px;
height: 20px;
line-height: 10px;
border-radius: 50%;
background: rgba(157, 162, 172, 1);
box-shadow: 0px 2px 8px 0px rgba(218, 220, 223, 0.7);
border: none;
cursor: pointer;
outline: none;
&:active {
box-shadow: 0px 0px 2px 2px rgba(218, 220, 223, 0.2) inset;
}
.trans(0.1s);
&:hover {
background: #1890ff;
.trans(0.1s);
}
}
.zoom-number {
color: #657180;
padding: 0 8px;
display: inline-block;
width: 46px;
text-align: center;
}
}
</style>
目录结构如下图所示
在router.js配置指向index.vue文件,即可访问
---------------------------------------------------------------------------------------------------------------
补充上面用到的tools.js文件,在src/libs目录下新建tools.js,复制下面的代码
/**
* @description 绑定事件 on(element, event, handler)
*/
export const on = (function () {
if (document.addEventListener) {
return function (element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false)
}
}
} else {
return function (element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler)
}
}
}
})()
/**
* @description 解绑事件 off(element, event, handler)
*/
export const off = (function () {
if (document.removeEventListener) {
return function (element, event, handler) {
if (element && event) {
element.removeEventListener(event, handler, false)
}
}
} else {
return function (element, event, handler) {
if (element && event) {
element.detachEvent('on' + event, handler)
}
}
}
})()
在org-view.vue 文件中引用到的on,off方法补充,将此文件copy到src/libs/目录下即可