ElTree增加右键菜单 上移下移

84 阅读3分钟

ElTree增加右键菜单 同一层级上移下移。上下文菜单有2种激活方式,右键和...

image.png

// 上下文菜单组件 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>

参考链接 blog.csdn.net/FunnyWhiteC…