Browse Source

Merge remote-tracking branch 'origin/master'

lipenghui 3 tháng trước cách đây
mục cha
commit
d3c618d3a8
2 tập tin đã thay đổi với 183 bổ sung2 xóa
  1. 41 2
      src/views/pms/device/ConfigDeviceAllot.vue
  2. 142 0
      src/views/pms/device/DeptTree2.vue

+ 41 - 2
src/views/pms/device/ConfigDeviceAllot.vue

@@ -39,7 +39,7 @@
         <div class="card" height="400px">
           <h3>部门列表</h3>
             <ContentWrap class="h-1/1" v-if="true" height="400px">
-              <DeptTree @node-click="handleDeptNodeClick" height="400px" :radio-value="selectedDeptId"/>
+              <DeptTree2 @node-click="handleDeptNodeClick" height="400px" :radio-value="selectedDeptId"/>
             </ContentWrap>
 
           <div class="action-bar">
@@ -94,7 +94,7 @@ import * as UserApi from "@/api/system/user";
 import {simpleUserList, UserVO} from "@/api/system/user";
 import {DICT_TYPE, getStrDictOptions} from "@/utils/dict";
 import {dateFormatter} from "@/utils/formatTime";
-import DeptTree from "@/views/system/user/DeptTree.vue";
+import DeptTree2 from "@/views/pms/device/DeptTree2.vue";
 
 defineOptions({ name: 'ConfigDeviceAllot' })
 
@@ -492,4 +492,43 @@ h3 {
   }
 }
 
+.card {
+  display: flex;
+  flex-direction: column;
+  height: 600px; /* 根据实际需要调整整体高度 */
+}
+
+.dept-select {
+  flex-shrink: 0; /* 防止搜索框被压缩 */
+}
+
+/* 左侧滚动区域 */
+.el-scrollbar {
+  flex: 1;
+  min-height: 0; /* 关键:允许内容区域收缩 */
+}
+
+/* 右侧树形容器 */
+.right-card {
+  display: flex;
+  flex-direction: column;
+}
+
+.dept-tree-container {
+  flex: 1;
+  min-height: 0; /* 关键:允许内容区域收缩 */
+  position: relative;
+}
+
+/* 确保内容区域填充高度 */
+.h-full {
+  height: 100%;
+}
+
+/* 调整树形组件内部滚动 */
+:deep(.el-tree) {
+  height: 100%;
+  overflow: auto;
+}
+
 </style>

+ 142 - 0
src/views/pms/device/DeptTree2.vue

@@ -0,0 +1,142 @@
+<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"
+      show-checkbox
+      @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'
+
+defineOptions({ name: 'SystemUserDeptTree' })
+
+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 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.getSimpleDeptList()
+  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)
+}
+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>