目录
1.简介
2.安装及使用
下载包
main.js全局引用
页面使用
数据要求
配合使用
3.基础使用
4.较深入使用
5.修改后的代码如下
1.简介
一个不算太简易的简易版组织架构图,组件依赖于vue-org-tree, 在此基础上将部分源代码进行优化修改。增加鼠标拖拽和鼠标滚轮缩放,并支持节点拖拽,以及节点编辑等功能。
优势:
1.支持整体拖拽、自定义展开组织树展开层级;
2.可进行节点搜索,显示搜索节点相关的组织树;
3.支持自定义节点样式,自定义新增、编辑、删除、节点是否拖拽、拖拽节点副本/节点;
做demo进行测试时发现一个缺点:当数据从1800条左右开始时,拖拽合并速度太快且频繁拖拽合并时,会报错数据找不到(感觉是上一轮拖拽数据还没有处理完,下一轮数据处理不了了,希望有大佬能解惑,能有好的解决办法)。
vue2的版本:zm-tree-org (gitee.io)
vue3的版本:Home | vue3-tree-org (sangtian152.github.io)
git源码:GitHub - sangtian152/zm-tree-org: 一个简易版组织架构图,组件依赖于vue-org-tree, 在此基础上将部分源代码进行优化修改。支持鼠标拖动,鼠标滚轮缩放,节点拖拽。
2.安装及使用
以vue2的版本示例。
下载包
npm i zm-tree-org -S
main.js全局引用
import Vue from 'vue';
import ZmTreeOrg from 'zm-tree-org';
import "zm-tree-org/lib/zm-tree-org.css";
Vue.use(ZmTreeOrg);
页面使用 <zm-tree-org></zm-tree-org>
//使用示例:修改背景色/文字颜色
<zm-tree-org :label-style="style">
</zm-tree-org>
数据要求
最外层(公司级)是Object,其子级及以下为Array。
orgData:{ id: 1, //组织id,必须
label: "xxx科技有限公司", //组织名称,必须
disabled: true, //是否可编辑
children: [ //子级
{
id: 2, //子级组织id,必须
pid: 1, //父级组织id,必须
label: "产品研发部", //组织名称,必须
expand: false, //当前节点下的节点是否默认展开
noDragging: true, //当前节点是够允许拖拽
children: []
}
}
配合使用
页面部分功能需结合elementUI使用。
下载
npm i element-ui -S
main.js全局引用
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
3.基础使用
(1)树形结构横向/纵向展开;
(2)树形结构展开/收起;
(3)组织树默认展开层级 or 单一某节点的子节点树展开/收起;
单一某节点的详细说明:
需对数据做处理,单一节点的子节点树不填写默认跟随:default-expand-level。
示例:{id: 6,pid: 2,label: "展开当前节点的组织树",expand: true}。
如组件绑定 :default-expand-level="num"
且 num+1 < 当前设置的层级 上述设置不生效;
num+1 >= 当前设置的层级 上述设置生效。
num+1举例:expand:false,即使 num+1 >= 当前设置的层级,当前节点的子节点树也会收起。
文中最开始的示例图全部展开后共为 2级子节点(num) + 1 级根节点(所以num需要加1)。:default-expand-level="1"时,代表展开到第一级子节点,即“产品研发部”及它的同层级。
(4)组织树 可编辑/不可编辑 or 单一某节点 可编辑/不可编辑;
单一某节点的详细说明:
需对数据做处理,子节点不填写默认为false(可编辑)。
子节点示例:{id: 6,pid: 2,label: "禁止编辑节点",disabled: true}。
如组件绑定 :disabled="true" 上述设置被覆盖;:disabled="false",上述设置生效。
(5)节点树整体 可拖拽/不可拖拽 or 单一某节点 可拖拽/不可拖拽
单一某节点的详细说明:
需对数据做处理,子组件不填写默认为false(可拖拽)。
示例:{id: 6,pid: 2,label: "禁止拖拽节点",noDragging: true}。
如组件绑定 :node-draggable="true" 上述设置也生效; :node-draggable="false",上述设置被覆盖。
(6)拖拽节点副本、当前位置保留节点本身 / 拖拽节点本身;
(7)仅拖动当前节点、子节点添加到当前节点 / 当前节点及子节点一起拖动;
(8)在线调整组织结构任意背景色;
(9)在线调整文字任意颜色;
//color为string类型 :label-style="style"
<el-color-picker v-model="style.color" size="small"></el-color-picker>
(10)搜索组织,同时显示当前组织的上级所有组织;
<input type="text" v-model="keyword" placeholder="请输入搜索内容" @keydown.enter="filter" />
filter(){ this.$refs.tree.filter(this.keyword) }
4.较深入使用
(1)自定义节点右键点击事件修改;
如仅修改name,则仅修改显示的文字,插件自带的【编辑】样式依然显示;
如修改command值,则绑定右键菜单弹出和【复制】【新增】【编辑】【删除】4个可自定义的绑定事件失效,需重写写入事件。
例如:仅将command:'edit'修改为command:'edit1',如果某子节点为不可编辑状态,点击右键,依然会显示菜单【复制】【编辑】,但点击【编辑】事件无反应。如果不想【编辑】功能显示,可以绑定vue的@contextmenu.prevent,对menus进行动态赋值。
// data值
menus: [{ name: '复制文本', command: 'copy' }, { name: '新增节点', command: 'add' }, { name: '编辑节点', command: 'edit1' }, { name: '删除节点', command: 'delete' }],
disaled: false,
//自定义节点绑定方法
<div class="tree-org-node__text node-label node" @contextmenu.prevent="terFun(node)"></div>
//动态赋值
terFun(node) {
console.log(node)
if (node.disabled || this.disaled) {
this.menus = [{ name: '复制文本', command: 'copy' }]
} else {
this.menus = [
{ name: '复制文本', command: 'copy' },
{ name: '新增节点', command: 'add' },
{ name: '编辑节点', command: 'edit1' },
{ name: '删除节点', command: 'delete' }
]
}
},
(2)自定义节点及自定义编辑节点。
在4-(1)的基础上去做,需要修改自定义点击右键事件的edit值,否则插件自带的【编辑节点】样式依然会闪烁显示。
———————— Html说明 ————————
<template slot-scope="{node}"> // 插件的自定义节点插槽
// 此处可修改组织树的节点样式,当前为默认
<div class="tree-org-node__text node-label node">
{{ node.label }} // 组织名称
// 此处的class为late的 div为自定义的【编辑节点】card
<div v-if="treeScope && !node.disabled && node.open" class="late">
<el-input type="textarea" placeholder="请输入内容" v-model="cardOne.label" maxlength="30" show-word-limit></el-input>
<div @click="close(node)">确定</div>
</div>
</div>
</template>
———————— data说明 除node.disabled外,可自定义————————
treeScope: false, //是否显示【自定义编辑】card
node.disabled: 查找当前节点可编辑/不可编辑的属性
cardOne: {}, //【自定义编辑】card组件信息
node.open: //自定义的节点【编辑属性值】
———————— function说明 ————————
@on-contextmenu="onMenus" //右键菜单点击事件
如修改【4、自定义节点右键点击菜单修改的command:'edit'值,则插件自带编辑事件失效,需自定义,自定义如下】
onMenus({ node, command }) {
// 自定义编辑
if (command === 'edit1' && !node.disabled) {
//显示【自定义编辑】card,将node赋值给cardOne
//自定义编辑--card
}
},
组件自带的默认编辑节点样式:
修改后的实现效果:
弹窗自定义编辑节点
标签自定义编辑节点
文章来源:https://www.toymoban.com/news/detail-651453.html
5.修改后的代码如下
<template>
<div class="all" @click="closeO">
<div style="display: flex; padding: 10px 0">
<div style="margin-right: 10px">
<el-switch v-model="horizontal"></el-switch> {{ horizontal? "横向": "纵向" }}
</div>
<div style="margin-right: 10px">
<el-switch v-model="collapsable"></el-switch> {{ collapsable? "可收起": "仅展开" }}
</div>
<div style="margin-right: 10px">
<el-switch v-model="disaled"></el-switch> {{ disaled? "禁止编辑": "可编辑" }}
</div>
<div style="margin-right: 10px">
<el-switch v-model="onlyOneNode"></el-switch> {{ onlyOneNode? "仅拖动当前节点": "拖动当前节点树" }}
</div>
<div style="margin-right: 10px">
<el-switch v-model="cloneNodeDrag"></el-switch> {{ cloneNodeDrag? "拖动节点副本": "拖动节点本身" }}
</div>
<div style="margin-right: 10px">
<el-switch v-model="pop"></el-switch> {{ pop? "弹窗修改节点": "标签修改节点" }}
</div>
</div>
<div style="padding-bottom: 10px" class="pickers">
背景色:
<el-color-picker v-model="style.background" size="small"></el-color-picker> 文字颜色:
<el-color-picker v-model="style.color" size="small"></el-color-picker>
搜索:
<input type="text" v-model="keyword" placeholder="请输入搜索内容" @keydown.enter="filter" />
</div>
<div class="lll">
<div ref="nodeOne" class="nodeOne">
<zm-tree-org ref="tree" :data="data" :disabled="disaled" :horizontal="horizontal" :collapsable="collapsable"
:label-style="style" :node-draggable="true" :default-expand-level="1" :only-one-node="onlyOneNode"
:clone-node-drag="cloneNodeDrag" :node-draging="nodeDragMove" :node-drag-end="nodeDragEnd" :toolBar="toolBar"
:filterNodeMethod="filterNodeMethod" @on-contextmenu="onMenus" @on-expand="onExpand"
@on-node-click="onNodeClick" @on-node-dblclick="onNodeDblclick" @on-node-copy="onNodeCopy"
:define-menus="menus">
<!-- 自定义节点内容 -->
<template slot-scope="{node}">
<div class="tree-org-node__text node-label node" @contextmenu.prevent="terFun(node)">
{{ node.label }}
<div v-if="treeScope && !node.disabled && node.open" class="late" id="lateId">
<el-input type="textarea" placeholder="请输入内容" v-model="cardOne.label" maxlength="30" show-word-limit>
</el-input>
<div @click="close(cardOne)" class="onCloseCss">确定</div>
</div>
</div>
</template>
<!-- 自定义展开按钮 -->
<template v-slot:expand="{ node }">
<div>{{ node.children.length }}</div>
</template>
</zm-tree-org>
</div>
</div>
</div>
</template>
<script>
import allList from '../../public/orgTree.json'
export default {
data() {
return {
toolBar: {
scale: false,
},
keyword: "",
menus: [{ name: '复制文本', command: 'copy' }, { name: '新增节点', command: 'add' }, { name: '编辑节点', command: 'edit1' }, { name: '删除节点', command: 'delete' }],
data: {},
horizontal: false,
collapsable: true,
onlyOneNode: false,//仅拖动当前节点
cloneNodeDrag: false,//是否拷贝节点拖拽 false仅拖拽 true拷贝+保留原位
expandAll: true,
disaled: false,
style: { color: "#fff", background: "#108ffe" },
treeScope: false,
//弹窗
pop: false,
//card组件信息
cardOne: {},
};
},
created() {
this.data = allList.orgAll
},
methods: {
//动态赋值
terFun(node) {
console.log(node)
if (node.disabled || this.disaled) { this.menus = [{ name: '复制文本', command: 'copy' }] } else {
this.menus = [{ name: '复制文本', command: 'copy' }, { name: '新增节点', command: 'add' }, { name: '编辑节点', command: 'edit1' }, { name: '删除节点', command: 'delete' }]
}
},
//右侧菜单点击事件
onMenus({ node, command }) {
//自定义编辑--弹窗编辑
let labelOld = node.label
if (command === 'edit1' && this.pop && !node.disabled) {
this.$prompt('修改当前信息', '编辑', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputValue: labelOld
}).then((val) => {
this.$message({
type: 'success',
message: '修改成功'
});
// this.editFun(this.data.children, node, val.value)
this.$set(node, 'label', val.value)
}).catch(action => {
if (action === 'cancel') {
this.$message({
type: 'info',
message: '取消输入'
});
}
});
//当前不允许编辑
} else if (node.disabled && command === 'edit1') {
this.$message({
type: 'info',
message: '当前内容不可编辑'
});
//自定义编辑--card
} else if (command === 'edit1' && !this.pop && !node.disabled) {
this.$set(node, 'open', true)
this.cardOne = node
this.treeScope = true
}
},
//自定义修改属性值
editFun(data, node, val) {
if (!data || !data.length) {
return;
}
for (let i = 0; i < data.length; i++) {
if (data[i].id === node.id) {
data[i].label = val;
break;
}
this.editFun(data[i].children, node, val);
}
},
//确定 关闭car界面
close(val) {
this.$delete(val, 'open')
this.treeScope = false
},
//颠倒编辑外区域,关闭
closeO(event) {
var currentCli = document.getElementById("lateId");
if (currentCli) {
if (!currentCli.contains(event.target)) {
this.treeScope = false;
}
}
},
//自定义card显示
filter() {
this.$refs.tree.filter(this.keyword);
},
filterNodeMethod(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
//展开事件
onExpand(e, data) {
},
nodeDragMove(data) {
},
nodeDragEnd(data, isSelf) {
// isSelf && this.$Message.info("移动到自身");
},
//点击节点
onNodeClick(e, data) {
// this.$Message.info(data.label);
},
onNodeDblclick() {
// this.$Message.info("双击节点");
},
onNodeCopy() {
// this.$Message.success("复制成功");
},
handleNodeAdd(node) {
// this.$Message.info("新增节点");
},
expandChange() {
this.toggleExpand(this.data, this.expandAll);
},
//递归创建节点树
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);
}
}
},
},
};
</script>
<style scoped>
.pickers {
display: flex;
}
.node {
position: relative;
}
.nodeOne {
height: 800px;
}
.late {
position: absolute;
top: 20px;
right: -100px;
width: 200px;
min-height: 100px;
z-index: 999;
background: #F2E2BE;
padding: 10px;
}
.onCloseCss {
width: 50px;
height: 30px;
text-align: center;
line-height: 30px;
background: #108FFE;
margin: 10px auto;
z-index: 999;
}
::v-deep .is-edit {
background: palegoldenrod !important;
}
</style>
.json数据文章来源地址https://www.toymoban.com/news/detail-651453.html
{
"orgAll":{
"id": 1,
"label": "xxx科技有限公司",
"disabled": true,
"children": [
{
"id": 2,
"pid": 1,
"label": "产品研发部",
"expand": false,
"children": [
{
"id": 6,
"pid": 2,
"label": "禁止编辑节点",
"disabled": true
},
{
"id": 8,
"pid": 2,
"label": "禁止拖拽节点",
"noDragging": true
},
{
"id": 7,
"pid": 2,
"label": "研发-后端",
"children": [
{
"id": 14,
"pid": 7,
"label": "后端1组"
},
{
"id": 15,
"pid": 7,
"label": "后端2组"
}
]
},
{
"id": 13,
"pid": 2,
"label": "研发-前端"
},
{
"id": 9,
"pid": 2,
"label": "产品经理"
},
{
"id": 10,
"pid": 2,
"label": "测试"
}
]
},
{
"id": 3,
"pid": 1,
"label": "客服部",
"children": [
{
"id": 11,
"pid": 3,
"label": "客服一部"
},
{
"id": 12,
"pid": 3,
"label": "客服二部"
}
]
},
{
"id": 4,
"pid": 1,
"label": "业务部"
},
{
"id": 5,
"pid": 1,
"label": "人力资源中心"
}
]
}
}
到了这里,关于zm-org-tree可拖拽的组织树,简易好上手的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!