ElTree增加右键菜单 同一层级上移下移。上下文菜单有2种激活方式,右键和...
// 上下文菜单组件 classificationContextMenu.vue
<template>
<div
v-show="visible"
:style="{
left: position.left + 'px',
top: position.top + 'px',
display: visible ? 'block' : 'none',
}"
class="context-menu"
>
<div
v-for="(item, i) in menuItems"
:key="i"
class="menu-item"
@click="item.action(rightClickItem)"
>
{{ item.name }}
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
interface Props {
menuItems: ContextMenuItem[]
}
export interface ContextMenuItem {
name: string
icon?: string
action: (rightClickItem: any) => void
}
const props = defineProps<Props>()
const visible = ref(false)
const rightClickItem = ref(null)
const position = ref({
top: 0,
left: 0,
})
const openMenu = (e: MouseEvent, item: any) => {
let menuCount = props.menuItems.length
let windowHeight = window.innerHeight
// console.log('777777', e)
visible.value = true
position.value.top = e.pageY - 100
// Math.min(
// e.pageY - 20,
// windowHeight - 20 - menuCount * 32
// )
position.value.left = e.pageX - 190
rightClickItem.value = item
}
const closeMenu = () => {
visible.value = false
}
watch(visible, () => {
if (visible.value) {
document.body.addEventListener('click', closeMenu)
} else {
document.body.removeEventListener('click', closeMenu)
}
})
defineExpose({ openMenu, closeMenu })
</script>
<style scoped lang="less">
.context-menu {
margin: 0;
background: #fff;
z-index: 112;
position: absolute;
list-style-type: none;
padding: 4px;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.3);
.menu-item {
padding: 0 15px;
height: 32px;
line-height: 32px;
color: rgb(29, 33, 41);
cursor: pointer;
}
.menu-item:hover {
background: var(--el-color-primary-light-9);
border-radius: 4px;
}
}
</style>
<template>
<div class="cwrap u-f">
<div class="left_tree bgwhite p-[10px] pb-[60px]">
<el-input
placeholder="请输入目录名称"
v-model="searchName"
@change="getLeftTree()"
/>
<el-tree
:data="treeData"
:props="{
label: 'label',
value: 'id',
}"
:default-expanded-keys="defaultExpandedKeys"
:highlight-current="true"
:height="508"
node-key="id"
@node-click="handleTreeNodeClick"
class="mt-[15px]"
ref="treeRef"
>
<template #default="{ node, data }">
<span class="custom-tree-node flex items-center justify-between">
<div @contextmenu.prevent="openMenu($event, node)">
{{ node.label }}
</div>
<div class="ml-[25px] pt-[4px]">
<i-ep-more-filled @click.stop="openMenu($event, node)" />
</div>
</span>
</template>
</el-tree>
<classificationContextMenu :menu-items="menuItems" ref="contextMenuRef" />
</div>
<div class="rcontent">
<dataIndexList
v-if="!isEnTableShow"
:activeNodeId="activeNodeId"
@sourceNameClick="sourceNameClick"
/>
<classificationColumn
:clickRow="clickRow"
v-else
@backClick="onBackClick"
/>
</div>
<!-- 上下文菜单新增弹窗 -->
<m-dialog
v-model="isAddDiaShow"
:title="activeNode.type === 'add' ? '新增目录' : '编辑目录'"
width="740px"
confirmText="确定"
@close="isAddDiaShow = false"
@confirm="handleSaveSubmit"
>
<classificationAddForm
:treeData="treeData"
:row="activeNode"
ref="addFormRef"
/>
</m-dialog>
</div>
</template>
<script setup lang="ts">
import {
fetchClassTree,
fetchClassDel,
fetchClassMove,
fetchClassRegisterTableList,
} from '@/api/dataIntegration'
import { useCommonStore } from '@/store/common'
import { watch, ref, nextTick } from 'vue'
import classificationColumn from '@/components/views/dataDir/dataClassification/classificationColumn.vue'
import dataIndexList from '@/components/views/dataDir/dataClassification/dataIndexList.vue'
import classificationContextMenu from '@/components/views/dataDir/dataClassification/classificationContextMenu.vue'
import classificationAddForm from '@/components/views/dataDir/dataClassification/classificationAddForm.vue'
const { proxy } = getCurrentInstance()
const commonStore = useCommonStore()
const router = useRouter()
const treeData = ref([])
let treeRef = ref(null)
let searchName = ref('') // 输入框
const activeNodeId = ref(null)
const defaultExpandedKeys = ref([])
let clickRow = ref()
let activeNode = ref({})
const isAddDiaShow = ref(false)
const contextMenuRef = ref(null)
const addFormRef = ref(null)
// 点击表格的英文名称 时候显示表列表
let isEnTableShow = ref(false)
// 渲染树
const getLeftTree = (refresh = true) => {
fetchClassTree({
projectId: commonStore.currentProject.id,
name: searchName.value,
})
.then(res => {
// 设置项目信息
if (res.code === 0) {
let list = [res.data]
const setLabel = arr => {
arr.forEach(item => {
item.label = `${item.name} (${item.registerCount})`
// item.id = item.projectId
if (item?.children?.length) {
setLabel(item.children)
}
})
}
list.forEach(item => {
// item.id = 0 || item.projectId
// item.projectId = 0
item.label = `${item.name} (${item.registerCount})`
if (item.children.length) {
setLabel(item.children)
}
})
const setExpandedKeys = obj => {
if (obj?.children?.length) {
defaultExpandedKeys.value.push(obj.children[0].id)
obj.children.forEach(d => {
setExpandedKeys(d)
})
}
}
defaultExpandedKeys.value = [list[0].id]
setExpandedKeys(res.data)
let innerId =
defaultExpandedKeys.value[defaultExpandedKeys.value.length - 1]
activeNodeId.value = innerId
nextTick(() => {
treeRef.value.setCurrentKey(innerId)
})
treeData.value = list
} else {
treeData.value = []
}
})
.catch(err => {
treeData.value = []
})
}
//
const sourceNameClick = row => {
isEnTableShow.value = true
clickRow.value = row
}
const onBackClick = () => {
isEnTableShow.value = false
}
getLeftTree()
//调接口实现上移 下移
const handleMove = (node, type) => {
fetchClassMove({
id: node.data.id,
type: type, // 1:上移 2:下移
}).then(res => {
getLeftTree()
})
}
const handleTreeNodeClick = data => {
console.log('data----', data)
activeNode.value = data
// 这里就是.id
activeNodeId.value = data.id
}
let menus = [
{
name: '新增目录',
action: (item: any) => {
activeNode.value = item
activeNode.value.type = 'add'
handleMenuAdd(item)
console.log('新增目录', item)
},
},
{
name: '编辑',
action: (item: any) => {
activeNode.value = item
activeNode.value.type = 'edit'
handleMenuEdit(item)
console.log('编辑', item)
},
},
{
name: '删除',
action: (item: any) => {
proxy
?.$confirm('是否确认删除”,点击确认后删除?', '提示')
.then(() => {
fetchClassDel({ id: item.data.id }).then(res => {
proxy?.$message({
message: '操作成功',
duration: 1000,
type: 'success',
})
getLeftTree()
})
})
.catch(() => {})
console.log('删除', item)
},
},
{
name: '上移',
action: (item: any) => {
handleMove(item, 1)
console.log('上移', item)
},
},
{
name: '下移',
action: (item: any) => {
handleMove(item, 2)
console.log('下移', item)
},
},
]
const menuItems = ref<any[]>([])
const openMenu = ($event, node) => {
// console.log('node-------', node, treeData.value)
// console.log('node------111-', node.id, treeData.value[0])
// 注意这里是node.data.id
if (node.data.id === treeData.value[0]?.id) {
menuItems.value = [
{
name: '新增目录',
action: (item: any) => {
handleMenuAdd(item)
console.log('新增目录', item)
},
},
]
} else {
menuItems.value = menus
}
contextMenuRef.value!.openMenu($event, node)
}
const handleMenuAdd = item => {
isAddDiaShow.value = true
}
const handleMenuEdit = item => {
isAddDiaShow.value = true
}
// 树新增目录弹窗确定
const handleSaveSubmit = () => {
addFormRef.value?.handleSubmit().then(async obj => {
proxy?.$message({
message: '操作成功',
duration: 1000,
type: 'success',
})
isAddDiaShow.value = false
getLeftTree()
})
}
watch(
() => commonStore.currentProject.id,
() => {
getLeftTree()
}
)
</script>
<style lang="less" scoped>
.cwrap {
.left_tree {
width: 170px;
height: 100%;
padding-top: 10px;
margin-right: 10px;
.list {
width: 100%;
padding-right: 10px;
box-sizing: border-box;
.txt {
max-width: 80px;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.rcontent {
width: calc(100% - 160px);
padding: 15px 15px;
box-sizing: border-box;
.rtitle {
font-size: 16px;
color: #333333;
font-weight: bold;
margin-bottom: 10px;
}
.btn_box {
padding: 30px 0 50px;
}
}
}
</style>