封装的组件:
<template>
<div class="tree-move-search">
<!-- 搜索框 -->
<div v-if="searchInputVisible" class="search-ctx">
<el-input
v-model="searchValue"
size="small"
clearable
:placeholder="placeholder"
suffix-icon="el-icon-search"
/>
</div>
<!-- 中间操作按钮 -->
<div class="btnWrap">
<slot name="btnWrap" />
</div>
<div v-show="!searchValue" class="tree-wrapper">
<el-tree
ref="moveTree"
:props="defaultProps"
check-on-click-node
:expand-on-click-node="false"
:data="treeData"
highlight-current
node-key="id"
draggable
:lazy="lazy"
:load="loadNode"
:allow-drop="allowDrop"
:filter-node-method="filterNode"
:default-expanded-keys="defaultExpandedKeys"
@node-click="nodeClick"
@node-drop="handleDrop"
>
<div slot-scope="{ node, data }" class="custom-tree-node text-overflow">
<!-- 自定义节点内容 -->
<slot name="nodeText" v-bind="data">
<div>
<span class="node-text ml5">{{ node.label }}</span>
</div>
</slot>
<!-- 节点右侧操作列表 -->
<div @click.stop="()=>{}">
<el-dropdown
class="operator"
trigger="click"
@command="(command)=>{
handleCommand(command,node)
}">
<span class="el-dropdown-link">
<i class="el-icon-more" />
</span>
<el-dropdown-menu slot="dropdown">
<slot name="operator" v-bind="node" />
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</el-tree>
</div>
<!-- 搜索内容展示 -->
<div v-show="searchValue" class="search-content">
<slot name="searchContent" />
</div>
</div>
</template>
<script>
export default {
components: {},
props: {
searchVal: {
type: String,
require: true,
},
searchInputVisible: {
type: Boolean,
default: true,
},
placeholder: {
type: String,
default: '请输入',
},
treeData: {
type: Array,
require: true,
},
defaultProps: {
type: Object,
default: () => ({
label: 'name',
children: 'children',
isLeaf: 'leaf',
}),
},
lazy: {
type: Boolean,
default: false,
},
loadNode: {
type: Function,
},
defaultExpandedKeys: {
type: Array,
default: () => [],
},
},
computed: {
searchValue: {
get() {
return this.searchVal
},
set(val) {
this.$emit('update:searchVal', val)
},
},
nameIndex() {
return (name) => {
return name.indexOf(this.searchValue)
}
},
},
watch: {
searchValue(val) {
this.$refs.moveTree?.filter(val)
},
},
created() {
},
mounted() {
this.$emit('TreeInstance', this.$refs.moveTree)
},
methods: {
filterNode(value, data) {
if (!value) return true
return data.name.indexOf(value) !== -1
},
nodeClick(...args) {
this.$emit('nodeClick', ...args)
},
// 搜索父节点
searchNodeById(nodes, targetId) {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
if (node.id === targetId) {
return node
}
if (node.children) {
const result = this.searchNodeById(node.children, targetId)
if (result) {
return result
}
}
}
return null
},
move(node, type) {
const data = node.data
const parent = node.parent
const childNodes = parent.childNodes
const cIndex = childNodes.findIndex((n) => n.data.id === data.id)
// 被移动的节点
const passiveNode = childNodes[cIndex]
// 移动完成后的上一个节点
let beforeNode = null
// 上移
if (type === 'up') {
// 参照节点
const referenceNode = childNodes[cIndex - 1]
beforeNode = childNodes[cIndex - 2]
this.$refs.moveTree.remove(passiveNode)
this.$refs.moveTree.insertBefore(passiveNode.data, referenceNode)
}
// 下移
else {
// 参照节点
const referenceNode = childNodes[cIndex + 1]
beforeNode = referenceNode
this.$refs.moveTree.remove(passiveNode)
this.$refs.moveTree.insertAfter(passiveNode.data, referenceNode)
}
console.log(passiveNode, 'passiveNode')
return {
orgId: passiveNode.data.id,
// 移动到第一个时,beforeNode是不存在的,所以这里要进行判断
targetBeforeOrgId: beforeNode ? beforeNode.data.id : '',
targetParentOrgId: passiveNode.data.parent,
}
},
handleCommand(command, node) {
let moveInfo = null
switch (command) {
case 'up':
moveInfo = this.move(node, 'up')
break
case 'down':
moveInfo = this.move(node, 'down')
break
}
this.$emit('triggerCommand', { command, node, moveInfo })
},
// tree拖拽成功完成时触发的事件
handleDrop(draggingNode, dropNode, dropType) {
console.log(draggingNode, dropNode, dropType)
const info = {
id: draggingNode.data.id,
targetBeforeId: dropNode.data.id,
targetParentId: dropNode.parent.data.id,
}
// 如果是before说明是拖到了第一个位置
if (dropType === 'before') {
info.targetBeforeOrgId = ''
}
console.log(info)
this.$emit('drop', info)
},
// 拖拽时判定目标节点能否被放置
// 'prev'、'inner' 和 'next',分前、插入、后
allowDrop(draggingNode, dropNode, type) {
console.log(draggingNode, dropNode, type)
if (draggingNode.level === dropNode.level) {
// 父节点相同
if (dropNode.parent.childNodes.find((item)=>item.data.id === draggingNode.data.id)) {
/*
这里或的顺序很重要
此时的写法表示:首先判断是否可以移动到某个元素的下方,然后再判断是否可以移动到某个元素的上方
*/
return type === 'next' || type === 'prev'
}
return false
}
// 不同级进行处理
return false
},
},
}
</script>
<style scoped lang="scss">
.tree-move-search{
padding: 20px;
.tree-wrapper{
margin-top: 20px;
.custom-tree-node{
display: flex;
justify-content: space-between;
width: 100%;
&:hover{
color: #4a60ec;
}
.node-text{
.search-value{
color: #4a60ec;
}
}
.operator:hover{
background: #f0f1f4;
}
}
}
.search-content{
margin-top: 20px;
}
}
</style>
组件的使用:
<template>
<div id="app">
<TreeMoveSearch :treeData="treeData" :defaultProps="defineProps">
<template #operator="node">
<el-dropdown-item
v-for="item in filterDropdownList(node)"
:key="item.label"
:command="item.type"
>
{{ item.label }}
</el-dropdown-item>
</template>
</TreeMoveSearch>
</div>
</template>
<script>
import TreeMoveSearch from '@/components/tree-move-search/index.vue'
export default{
components:{
TreeMoveSearch
},
data() {
return {
treeData: [{
id: 1,
label: '一级 1',
children: [{
id: 4,
label: '二级 1-1',
children: [{
id: 9,
label: '三级 1-1-1'
}, {
id: 10,
label: '三级 1-1-2'
}]
}]
}, {
id: 2,
label: '一级 2',
children: [{
id: 5,
label: '二级 2-1'
}, {
id: 6,
label: '二级 2-2'
}]
}, {
id: 3,
label: '一级 3',
children: [{
id: 7,
label: '二级 3-1'
}, {
id: 8,
label: '二级 3-2'
}]
}],
defineProps:{
label:'label',
children:'children'
}
}
},
methods:{
// 过滤操作按钮
filterDropdownList(node) {
console.log(node);
const data = node.data
const parent = node.parent
const childNodes = parent.childNodes
const cLen = childNodes.length
const cIndex = childNodes.findIndex((n) => n.data.id === data.id)
// 根节点
if (cIndex === 0 && cIndex === cLen - 1) {
return [
{ label: '添加子组织', type: 'add' },
{ label: '修改组织', type: 'update' },
]
} else if (cIndex === 0) {
//第一个
return [
{ label: '添加子组织', type: 'add' },
{ label: '修改组织', type: 'update' },
{ label: '下移', type: 'down' },
{ label: '删除', type: 'delete' },
]
} else if (cIndex === cLen - 1 ) {
// 最后一个
return [
{ label: '添加子组织', type: 'add' },
{ label: '修改组织', type: 'update' },
{ label: '上移', type: 'up' },
{ label: '删除', type: 'delete' },
]
}
return [
{ label: '添加子组织', type: 'add' },
{ label: '修改组织', type: 'update' },
{ label: '上移', type: 'up' },
{ label: '下移', type: 'down' },
{ label: '删除', type: 'delete' },
]
},
}
}
</script>
<style scoped>
</style>