From a082e59c3e7713caf78c29eb292254418ad5b228 Mon Sep 17 00:00:00 2001 From: chenxing Date: Wed, 16 Sep 2026 17:46:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(tree):=20=E4=BF=AE=E5=A4=8D=E5=B7=A6?= =?UTF-8?q?=E4=BE=A7=E6=A0=91=E6=BB=9A=E5=8A=A8=E6=9D=A1=E5=A4=B1=E6=95=88?= =?UTF-8?q?=E4=B8=8E=E5=8F=B6=E5=AD=90=E8=8A=82=E7=82=B9=E8=AF=AF=E6=98=BE?= =?UTF-8?q?=20+/=E2=88=92=EF=BC=8C=E7=BB=9F=E4=B8=80=E6=9D=83=E9=99=90?= =?UTF-8?q?=E4=B8=80=E8=A7=88=E8=BF=9E=E6=8E=A5=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 滚动条(上次改动引入的回归): - MyTree 根节点加 .my-tree 类并打通高度链(flex 纵向 + .ztree 承担滚动), 父级定高时滚动条只落在树节点区域;父级 auto 的卡片内树行为不变 - GroupTreeSearchPanel 的 .tree-body 改 flex 纵向传递高度,删除已失效的 .ztree 规则 叶子节点 +/−(以组用户管理左侧树为参考统一): - zTree 只要 children 是数组即强制 isParent=true,故空数组必须不挂键; 修正 GroupAuth / AppModuleCheckTree / PermissionView 共 9 处节点构造 - MyTree 初始化前加节点规范化兜底,防止后续新增树重犯 - removeNode 删除末个子节点后同步父节点父/叶状态,不再残留 +/− 权限一览: - 删除 li{line-height:26px} 覆盖(zTree 图标按 21px 节距,26px 会让连接线出现断口) - 左列改用页面级 permission-tree 类:flex 纵向 + overflow:hidden, 消除整列滚动与外层裁切,滚动收敛到树体 --- src/components/GroupTreeSearchPanel.vue | 9 +- src/components/MyTree.vue | 49 ++++++++- src/views/GroupAuth.vue | 16 ++- src/views/PermissionView.vue | 109 ++++++++++---------- src/views/group-user/index.vue | 3 +- src/views/role-authz/AppModuleCheckTree.vue | 18 +++- 6 files changed, 135 insertions(+), 69 deletions(-) diff --git a/src/components/GroupTreeSearchPanel.vue b/src/components/GroupTreeSearchPanel.vue index 2da0ea7..b5e6458 100644 --- a/src/components/GroupTreeSearchPanel.vue +++ b/src/components/GroupTreeSearchPanel.vue @@ -285,10 +285,13 @@ defineExpose({ .tree-body { flex: 1; min-height: 0; + display: flex; + flex-direction: column; } -.tree-body :deep(.ztree) { - height: 100%; - overflow-y: auto; +/* 滚动职责统一由 MyTree(.my-tree > .ztree) 承担:此处只负责把可用高度传给子组件 */ +.tree-body > .my-tree { + flex: 1; + min-height: 0; } diff --git a/src/components/MyTree.vue b/src/components/MyTree.vue index 5c32ab2..9ec4355 100644 --- a/src/components/MyTree.vue +++ b/src/components/MyTree.vue @@ -1,9 +1,35 @@ + + diff --git a/src/views/group-user/index.vue b/src/views/group-user/index.vue index a1bd411..ecb3f41 100644 --- a/src/views/group-user/index.vue +++ b/src/views/group-user/index.vue @@ -371,7 +371,8 @@ function onSortSaved() { left: 348px !important; } -/* 左侧树容器改为 flex 纵向布局并收起自身滚动,把滚动条收敛到树节点区域(.ztree 自带 overflow-y:auto), +/* 左侧树容器改为 flex 纵向布局并收起自身滚动,把滚动条收敛到树节点区域 + (滚动体是 MyTree 内部的 .ztree,见 components/MyTree.vue), 避免树过长时把顶部 opera-ul 操作按钮条滚出屏幕;仅作用于本页,不影响全局 .tree-container */ .tree-wrapper.group-user-page .tree-container.group-user-tree { display: flex; diff --git a/src/views/role-authz/AppModuleCheckTree.vue b/src/views/role-authz/AppModuleCheckTree.vue index c70041a..885be3b 100644 --- a/src/views/role-authz/AppModuleCheckTree.vue +++ b/src/views/role-authz/AppModuleCheckTree.vue @@ -68,6 +68,7 @@ type AuthzTreeNode = { nodeType: "app" | "module"; rawId: number; appId: number; + isParent?: boolean; children?: AuthzTreeNode[]; }; type CategoryTreeItem = { cat: CategoryItem; treeData: AuthzTreeNode[]; weight: number }; @@ -115,8 +116,10 @@ function isModuleAllChecked(node: AppModuleVo): boolean { if (!node.checked) return false; return (node.children || []).every((c) => isModuleAllChecked(c)); } +// 空 children 不挂键:zTree 只要见到 children 是数组就强制 isParent=true 而渲染 +/−(叶子也会出现展开图标) function buildModuleNode(m: AppModuleVo, appId: number): AuthzTreeNode { - return { + const kids = (m.children || []).map((c) => buildModuleNode(c, appId)); + const node: AuthzTreeNode = { id: `mod_${m.id}`, name: String(m.name || ""), open: true, @@ -124,21 +127,26 @@ function buildModuleNode(m: AppModuleVo, appId: number): AuthzTreeNode { nodeType: "module", rawId: Number(m.id), appId, - children: (m.children || []).map((c) => buildModuleNode(c, appId)), + isParent: kids.length > 0, }; + if (kids.length > 0) node.children = kids; + return node; } function buildAppNode(app: AppItem): AuthzTreeNode { const appId = Number(app.id); - return { + const kids = (app.modules || []).map((m) => buildModuleNode(m, appId)); + const node: AuthzTreeNode = { id: `app_${appId}`, name: String(app.name || ""), - open: false, // 一级默认闭合,点击才展开模块 + open: false, checked: isAppChecked(app), nodeType: "app", rawId: appId, appId, - children: (app.modules || []).map((m) => buildModuleNode(m, appId)), + isParent: kids.length > 0, }; + if (kids.length > 0) node.children = kids; + return node; } // ---------------- 数据加载 ----------------