DeptTree.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <template>
  2. <div class="head-container" style="display: flex;flex-direction: row;">
  3. <el-input v-model="deptName" class="mb-18px" clearable placeholder="请输入部门名称">
  4. <template #prefix>
  5. <Icon icon="ep:search" />
  6. </template>
  7. </el-input>
  8. </div>
  9. <div ref="treeContainer" class="tree-container">
  10. <el-tree
  11. ref="treeRef"
  12. :data="deptList"
  13. :expand-on-click-node="false"
  14. :filter-node-method="filterNode"
  15. :props="defaultProps"
  16. :default-expanded-keys="firstLevelKeys"
  17. highlight-current
  18. node-key="id"
  19. @node-click="handleNodeClick"
  20. @node-contextmenu="handleRightClick"
  21. style="height: 52em"
  22. />
  23. </div>
  24. <div
  25. v-show="menuVisible"
  26. class="custom-menu"
  27. :style="{ left: menuX + 'px', top: menuY + 'px' }"
  28. >
  29. <ul>
  30. <li @click="handleMenuClick('add')">新增子节点</li>
  31. <li @click="handleMenuClick('edit')">重命名</li>
  32. <li @click="handleMenuClick('delete')">删除</li>
  33. </ul>
  34. </div>
  35. </template>
  36. <script lang="ts" setup>
  37. import { ElTree } from 'element-plus'
  38. import * as DeptApi from '@/api/system/dept'
  39. import { defaultProps, handleTree } from '@/utils/tree'
  40. import { useTreeStore } from '@/store/modules/usersTreeStore'
  41. defineOptions({ name: 'SystemUserDeptTree' })
  42. const deptName = ref('')
  43. const deptList = ref<Tree[]>([]) // 树形结构
  44. const treeRef = ref<InstanceType<typeof ElTree>>()
  45. const menuVisible = ref(false);
  46. const menuX = ref(0);
  47. const menuY = ref(0);
  48. const firstLevelKeys = ref([])
  49. let selectedNode = null;
  50. const treeStore = useTreeStore();
  51. const handleRightClick = (event, { node, data }) => {
  52. event.preventDefault();
  53. menuX.value = event.clientX;
  54. menuY.value = event.clientY;
  55. selectedNode = data; // 存储当前操作的节点数据 ‌:ml-citation{ref="7" data="citationList"}
  56. //menuVisible.value = true;
  57. };
  58. const treeContainer = ref(null)
  59. const setHeight = () => {
  60. if (!treeContainer.value) return
  61. const windowHeight = window.innerHeight
  62. const containerTop = treeContainer.value.offsetTop
  63. treeContainer.value.style.height = `${windowHeight * 0.78}px` // 60px 底部预留
  64. }
  65. const handleMenuClick = (action) => {
  66. switch(action) {
  67. case 'add':
  68. // 调用新增节点逻辑 ‌:ml-citation{ref="4" data="citationList"}
  69. break;
  70. case 'edit':
  71. // 调用编辑节点逻辑 ‌:ml-citation{ref="7" data="citationList"}
  72. break;
  73. case 'delete':
  74. // 调用删除节点逻辑 ‌:ml-citation{ref="4" data="citationList"}
  75. break;
  76. }
  77. menuVisible.value = false;
  78. };
  79. /** 获得部门树 */
  80. const getTree = async () => {
  81. const res = await DeptApi.getSimpleDeptList()
  82. deptList.value = []
  83. deptList.value.push(...handleTree(res))
  84. firstLevelKeys.value = deptList.value.map(node => node.id);
  85. }
  86. /** 基于名字过滤 */
  87. const filterNode = (name: string, data: Tree) => {
  88. if (!name) return true
  89. return data.name.includes(name)
  90. }
  91. /** 处理部门被点击 */
  92. const handleNodeClick = async (row: { [key: string]: any }) => {
  93. emits('node-click', row)
  94. treeStore.setSelectedId(row.id);
  95. }
  96. const emits = defineEmits(['node-click'])
  97. /** 监听deptName */
  98. watch(deptName, (val) => {
  99. treeRef.value!.filter(val)
  100. })
  101. /** 初始化 */
  102. onMounted(async () => {
  103. await getTree()
  104. setHeight()
  105. window.addEventListener('resize', setHeight)
  106. })
  107. onUnmounted(() => {
  108. window.removeEventListener('resize', setHeight)
  109. })
  110. </script>
  111. <style lang="scss" scoped>
  112. .custom-menu {
  113. position: fixed;
  114. background: white;
  115. border: 1px solid #ccc;
  116. box-shadow: 2px 2px 5px rgba(0,0,0,0.1);
  117. z-index: 1000;
  118. }
  119. .custom-menu ul {
  120. list-style: none;
  121. padding: 0;
  122. margin: 0;
  123. }
  124. .custom-menu li {
  125. padding: 8px 20px;
  126. cursor: pointer;
  127. }
  128. .custom-menu li:hover {
  129. background: #f5f5f5;
  130. }
  131. .tree-container {
  132. overflow-y: auto;
  133. min-width: 100%;
  134. border: 1px solid #e4e7ed;
  135. border-radius: 4px;
  136. }
  137. </style>