| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 | <template>  <div class="head-container" style="display: flex;flex-direction: row;">    <el-input v-model="deptName" class="mb-18px" clearable placeholder="请输入部门名称">      <template #prefix>        <Icon icon="ep:search" />      </template>    </el-input>  </div>  <div ref="treeContainer" class="tree-container">    <el-tree      ref="treeRef"      :data="deptList"      :expand-on-click-node="false"      :filter-node-method="filterNode"      :props="defaultProps"      :default-expanded-keys="firstLevelKeys"      highlight-current      node-key="id"      @node-click="handleNodeClick"      @node-contextmenu="handleRightClick"      style="height: 52em"    />  </div>  <div    v-show="menuVisible"    class="custom-menu"    :style="{ left: menuX + 'px', top: menuY + 'px' }"  >    <ul>      <li @click="handleMenuClick('add')">新增子节点</li>      <li @click="handleMenuClick('edit')">重命名</li>      <li @click="handleMenuClick('delete')">删除</li>    </ul>  </div></template><script lang="ts" setup>import { ElTree } from 'element-plus'import * as DeptApi from '@/api/system/dept'import { defaultProps, handleTree } from '@/utils/tree'import { useTreeStore } from '@/store/modules/usersTreeStore'import {specifiedSimpleDepts} from "@/api/system/dept";defineOptions({ name: 'DeptTree2' })const deptName = ref('')const deptList = ref<Tree[]>([]) // 树形结构const treeRef = ref<InstanceType<typeof ElTree>>()const menuVisible = ref(false);const menuX = ref(0);const menuY = ref(0);const firstLevelKeys = ref([])let selectedNode = null;const treeStore = useTreeStore();const handleRightClick = (event, { node, data }) => {  event.preventDefault();  menuX.value = event.clientX;  menuY.value = event.clientY;  selectedNode = data; // 存储当前操作的节点数据 :ml-citation{ref="7" data="citationList"}  //menuVisible.value = true;};const treeContainer = ref(null)const setHeight = () => {  if (!treeContainer.value) return  const windowHeight = window.innerHeight  const containerTop = treeContainer.value.offsetTop  treeContainer.value.style.height = `${windowHeight * 0.78}px` // 60px 底部预留}const handleMenuClick = (action) => {  switch(action) {    case 'add':      // 调用新增节点逻辑 :ml-citation{ref="4" data="citationList"}      break;    case 'edit':      // 调用编辑节点逻辑 :ml-citation{ref="7" data="citationList"}      break;    case 'delete':      // 调用删除节点逻辑 :ml-citation{ref="4" data="citationList"}      break;  }  menuVisible.value = false;};/** 获得部门树 */const getTree = async () => {  const res = await DeptApi.specifiedSimpleDepts(157)  deptList.value = []  deptList.value.push(...handleTree(res))  firstLevelKeys.value = deptList.value.map(node => node.id);}/** 基于名字过滤 */const filterNode = (name: string, data: Tree) => {  if (!name) return true  return data.name.includes(name)}/** 处理部门被点击 */const handleNodeClick = async (row: { [key: string]: any }) => {  emits('node-click', row)  treeStore.setSelectedId(row.id);}const emits = defineEmits(['node-click'])/** 监听deptName */watch(deptName, (val) => {  treeRef.value!.filter(val)})/** 初始化 */onMounted(async () => {  await getTree()  setHeight()  window.addEventListener('resize', setHeight)})onUnmounted(() => {  window.removeEventListener('resize', setHeight)})</script><style lang="scss" scoped>.custom-menu {  position: fixed;  background: white;  border: 1px solid #ccc;  box-shadow: 2px 2px 5px rgba(0,0,0,0.1);  z-index: 1000;}.custom-menu ul {  list-style: none;  padding: 0;  margin: 0;}.custom-menu li {  padding: 8px 20px;  cursor: pointer;}.custom-menu li:hover {  background: #f5f5f5;}.tree-container {  overflow-y: auto;  min-width: 100%;  border: 1px solid #e4e7ed;  border-radius: 4px;}</style>
 |