From f01e6d926fa2239da3c0557871cd820c377cd8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=85=B4=E5=87=AF?= <2448379534@qq.com> Date: Wed, 22 Apr 2026 18:04:51 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=87=E9=B1=BC=E8=AE=BE=E6=96=BD=E6=9D=83?= =?UTF-8?q?=E9=99=90-=E9=A1=B5=E9=9D=A2=E9=80=BB=E8=BE=91=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E6=9B=B4=E6=94=B9=EF=BC=8C=E6=8E=A5=E5=8F=A3=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=EF=BC=8C=E5=AE=8C=E5=96=84=E5=AD=97=E5=85=B8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/dict/index.ts | 8 + frontend/src/api/user/index.ts | 24 + frontend/src/views/system/user/index.vue | 543 +++++++++++++++-------- frontend/vite.config.ts | 6 +- 4 files changed, 397 insertions(+), 184 deletions(-) diff --git a/frontend/src/api/dict/index.ts b/frontend/src/api/dict/index.ts index 46ead79..2b4d2a4 100644 --- a/frontend/src/api/dict/index.ts +++ b/frontend/src/api/dict/index.ts @@ -101,6 +101,14 @@ export function changeItemOrder(params:any) { params: params }); } +//根据字典编号查询字典项列表 +export function getDictItemsByCode(params:any) { + return request({ + url: '/system/dictionary/getDictItemsByCode', + method: 'get', + params: params + }); +} /** * 获取字典类型分页列表 * diff --git a/frontend/src/api/user/index.ts b/frontend/src/api/user/index.ts index 367454c..1a7ab4a 100644 --- a/frontend/src/api/user/index.ts +++ b/frontend/src/api/user/index.ts @@ -102,6 +102,30 @@ export function updatePassword (queryParams:any) { params: queryParams, }); } +//获取过鱼设施权限树 +export function getFishtree(queryParams:any) { + return request({ + url: '/env/tree/rvcdEng', + method: 'get', + params: queryParams + }); +} +//保存权限 +export function saveFishqvan(queryParams:any) { + return request({ + url: '/data/userDataScope/batchAdd', + method: 'post', + data: queryParams + }); +} +//查询用户有效权限 +export function getuserdata(queryParams:any) { + return request({ + url: '/data/userDataScope/getValidPermissions', + method: 'get', + params: queryParams + }); +} /** * 登录成功后获取用户信息(昵称、头像、权限集合和角色集合) diff --git a/frontend/src/views/system/user/index.vue b/frontend/src/views/system/user/index.vue index 02a4ab5..388b4ea 100644 --- a/frontend/src/views/system/user/index.vue +++ b/frontend/src/views/system/user/index.vue @@ -15,8 +15,14 @@ import { addUsers, updataUser, setpass, - delChoise + delChoise, + getFishtree, + saveFishqvan, + getuserdata } from "@/api/user"; +import { + getDictItemsByCode +} from "@/api/dict"; import { ElMessageBox, ElMessage } from "element-plus"; import { useAppStore } from '@/store/modules/app'; import Page from '@/components/Pagination/page.vue' @@ -267,6 +273,7 @@ function setpassword(row: any) { }) }) userid.value = row.id + } //过去设施权限维护 @@ -281,6 +288,28 @@ function openFishway(row: any) { // 临时模拟数据 getFishTree() // 打开对话框,watch 会处理回显逻辑 + fishhui.value = [] + tableDatafish.value = [] + getuserdata({ userId: userId.value }).then((res: any) => { + console.log(res) + if (res.code == 0) { + res.data.forEach((item: any) => { + fishhui.value.push(item.orgId) + tableDatafish.value.push({ + name: item.orgName, + type: item.orgType, // 默认类型为'站点' + id: item.orgId.toString(), // 使用code作为id + code: item.orgId, + path: item.path, + userId: item.userId, + parentId: item.parentId, + orgLevel: item.orgLevel, + permissionType: item.permissionType // 默认选择读权限 + }) + }) + + } + }) } @@ -299,14 +328,28 @@ watch(fishway, (newVal) => { // 延迟一点再设置,确保清除生效 setTimeout(() => { fishTreeRef.value.setCheckedKeys(fishhui.value, false); - // 验证回显结果 const checkedKeys = fishTreeRef.value.getCheckedKeys(); const halfCheckedKeys = fishTreeRef.value.getHalfCheckedKeys(); console.log('回显完成 - 全选节点:', checkedKeys); console.log('回显完成 - 半选节点:', halfCheckedKeys); }, 100); + } else if (fishTreeRef.value) { + // 如果fishhui.value为空,也需要清空表格数据 + setTimeout(() => { + fishTreeRef.value.setCheckedKeys([], false); + fishTableData.value = []; + }, 100); } + fishTableData.value = tableDatafish.value + // if (fishhui.value.length > 0 && activeName.value == '1') { + // isCompanyDisabled.value = true + // } else if (fishhui.value.length > 0 && activeName.value == '2') { + // isBaseDisabled.value = true + // } else { + // isCompanyDisabled.value = false + // isBaseDisabled.value = false + // } }, 500); } else { // 关闭时清空回显数据 @@ -317,105 +360,65 @@ watch(fishway, (newVal) => { if (fishTreeRef.value) { fishTreeRef.value.setCheckedKeys([], false); } + fishTableData.value = []; }, 300); } }); function fishHandleClose() { fishway.value = false + fishTableData.value = [] + fishData.value = [] } //过鱼设施type分类 -const activeName = ref('1') +// const activeName = ref('1') // 控制标签页禁用的状态(根据你的业务需求设置) -const isBaseDisabled = ref(false) // 基地标签是否禁用 -const isCompanyDisabled = ref(false) // 公司标签是否禁用 +// const isBaseDisabled = ref(false) // 基地标签是否禁用 +// const isCompanyDisabled = ref(false) // 公司标签是否禁用 -const handleClick = (tab: any, event: any) => { - // tab.props.name - console.log(tab.props.name, event) - activeName.value = tab.props.name - treeInput.value = '' - getFishTree() -} +// const handleClick = (tab: any, event: any) => { +// ElMessageBox.confirm("切换后现有数据将清空,是否切换", "删除提示", { +// confirmButtonText: "确定", +// cancelButtonText: "取消", +// type: "warning", +// }) +// .then(() => { +// fishhui.value = [] +// fishTreeDialog.value = true +// console.log(tab.props.name, event) +// activeName.value = tab.props.name +// treeInput.value = '' +// getFishTree() +// }) +// .catch(() => { +// // 用户取消删除 +// }); + +// } //获取过鱼设施权限 const treeInput = ref('') const fishProps = { children: 'children', - label: 'label', + label: 'name', } -const fishData: Tree[] = [ - { - id: 1, - label: 'Level one 1', - children: [ - { - id: 4, - label: 'Level two 1-1', - children: [ - { - id: 9, - label: 'Level three 1-1-1', - }, - { - id: 10, - label: 'Level three 1-1-2', - }, - ], - }, - ], - }, - { - id: 2, - label: 'Level one 2', - children: [ - { - id: 5, - label: 'Level two 2-1', - }, - { - id: 6, - label: 'Level two 2-2', - }, - ], - }, - { - id: 3, - label: 'Level one 3', - children: [ - { - id: 7, - label: 'Level two 3-1', - }, - { - id: 8, - label: 'Level two 3-2', - }, - ], - }, -] +const fishData: any = ref([]) //获取左侧树的数据 +// const huixiandata = ref([]) +let tableDatafish: any = [] function getFishTree() { fishTreeDialog.value = true const params = { - type: activeName.value, - name: treeInput.value + // type:'1', + engName: treeInput.value } + getFishtree(params).then((res: any) => { + if (res.code == 0) { + fishData.value = res.data + } + }) fishTreeDialog.value = false - // fishhui.value = [1, 5]; - if (fishhui.value.length > 0 && activeName.value == '1') { - isCompanyDisabled.value = true - } else if (fishhui.value.length > 0 && activeName.value == '2') { - isBaseDisabled.value = true - } else { - isCompanyDisabled.value = false - isBaseDisabled.value = false - } - // console.log('设置回显ID:', fishhui.value); - console.log(params) - // getFishTreeData().then((res) => { - // fishData.value = res.data.data - // }) + } // 过鱼设施权限维护 - 获取所有子节点ID的辅助函数 @@ -423,7 +426,7 @@ function getAllChildrenIds(node: any): number[] { const ids: number[] = []; if (node.children && node.children.length > 0) { node.children.forEach((child: any) => { - ids.push(child.id); + ids.push(child.code); if (child.children && child.children.length > 0) { ids.push(...getAllChildrenIds(child)); } @@ -439,14 +442,14 @@ function areAllChildrenChecked(parentNode: any, checkedKeysArray: any[]): boolea return false; } // 检查所有子节点ID是否都在已选中的keys中 - return allChildrenIds.every(id => checkedKeysArray.includes(id)); + return allChildrenIds.every(code => checkedKeysArray.includes(code)); } // 过鱼设施权限维护 - 检查节点是否被其他父节点包含 function isNodeContainedByOtherParent(nodeId: number, parentIds: number[], allCheckedNodes: any[]): boolean { // 遍历所有被标记为"全选"的父节点 for (const parentId of parentIds) { - const parentNode = allCheckedNodes.find((n: any) => n.id === parentId); + const parentNode = allCheckedNodes.find((n: any) => n.code === parentId); if (parentNode) { const childrenIds = getAllChildrenIds(parentNode); // 如果当前节点是某个父节点的子节点,则返回true @@ -458,38 +461,17 @@ function isNodeContainedByOtherParent(nodeId: number, parentIds: number[], allCh return false; } -// 过鱼设施权限维护 - 处理复选框选中事件 -function handleFishCheckChange(checkedNode: any, checkedInfo: any) { - console.log('当前操作的节点:', checkedNode); - console.log('选中的info:', checkedInfo); - - const treeInstance = treeRef.value; - if (!treeInstance) return; - - // 从 checkedInfo 中获取选中的keys数组 - const checkedKeysArray = checkedInfo.checkedKeys || []; - const allCheckedNodes = checkedInfo.checkedNodes || []; - - console.log('选中的keys数组:', checkedKeysArray); - console.log('选中的节点数组:', allCheckedNodes); - - // 最终结果ID数组 +// 统一处理选中IDs的过滤逻辑:如果父节点的所有子节点都被选中,只保留父节点ID +function filterSelectedIds(checkedKeysArray: number[], allCheckedNodes: any[]): number[] { const resultIds: number[] = []; - - // 第一阶段:找出所有"子节点全部被选中"的父节点 const fullySelectedParentIds: number[] = []; const filteredNodeIds = new Set(); + // 第一阶段:找出所有"子节点全部被选中"的父节点 allCheckedNodes.forEach((node: any) => { - - // 如果该节点有子节点(是父节点) if (node.children && node.children.length > 0) { - // 检查该节点下的所有子节点是否都被选中 if (areAllChildrenChecked(node, checkedKeysArray)) { - // 记录这个父节点 - fullySelectedParentIds.push(node.id); - - // 标记所有子节点为"应过滤" + fullySelectedParentIds.push(node.code); const childrenIds = getAllChildrenIds(node); childrenIds.forEach(childId => { filteredNodeIds.add(childId); @@ -498,13 +480,9 @@ function handleFishCheckChange(checkedNode: any, checkedInfo: any) { } }); - console.log('全选父节点IDs:', fullySelectedParentIds); - console.log('应过滤的子节点IDs:', Array.from(filteredNodeIds)); - // 第二阶段:添加结果 // 1. 先添加所有"全选"的父节点(但要排除被其他父节点包含的) fullySelectedParentIds.forEach(parentId => { - // 检查该父节点是否被其他父节点包含 if (!isNodeContainedByOtherParent(parentId, fullySelectedParentIds, allCheckedNodes)) { resultIds.push(parentId); } @@ -512,69 +490,80 @@ function handleFishCheckChange(checkedNode: any, checkedInfo: any) { // 2. 添加未被过滤的独立节点(叶子节点或未全选的父节点) allCheckedNodes.forEach((node: any) => { - // 如果该节点没有被标记为过滤,且不在resultIds中 - if (!filteredNodeIds.has(node.id) && !resultIds.includes(node.id)) { - // 再次检查:如果该节点是某个全选父节点的子节点,也要过滤 - if (!isNodeContainedByOtherParent(node.id, fullySelectedParentIds, allCheckedNodes)) { - resultIds.push(node.id); + if (!filteredNodeIds.has(node.code) && !resultIds.includes(node.code)) { + if (!isNodeContainedByOtherParent(node.code, fullySelectedParentIds, allCheckedNodes)) { + resultIds.push(node.code); } } }); + console.log('过滤后的IDs:', resultIds); + return resultIds; +} + +// 过鱼设施权限维护 - 处理复选框选中事件 +function handleFishCheckChange(checkedNode: any, checkedInfo: any) { + const treeInstance = fishTreeRef.value; + if (!treeInstance) return; + + // 从 checkedInfo 中获取选中的keys数组和节点 + const checkedKeysArray = checkedInfo.checkedKeys || []; + const allCheckedNodes = checkedInfo.checkedNodes || []; + + // 使用统一的过滤逻辑处理IDs + const resultIds = filterSelectedIds(checkedKeysArray, allCheckedNodes); + console.log('最终获取的IDs:', resultIds); - if (resultIds.length > 0 && activeName.value == '1') { - isCompanyDisabled.value = true - } else if (resultIds.length > 0 && activeName.value == '2') { - isBaseDisabled.value = true - } else { - isCompanyDisabled.value = false - isBaseDisabled.value = false - } - // 这里可以根据业务需求使用resultIds,比如调用接口保存权限 - // saveFishPermissions(resultIds); - getFishTableData() - tableids.value = resultIds + // 更新表格数据 + getFishTableData(resultIds); + tableids.value = resultIds; } //过鱼设施权限 -const fishTableData = ref([ - { - name: '过鱼设施权限1', - type: '站点', - id: 'fish_along_point1', - radio: '1' - }, - { - name: '过鱼设施权限2', - type: '站点', - id: 'fish_along_point2', - radio: '2' - }, { - name: '过鱼设施权限3', - type: '站点', - id: 'fish_along_point3', - radio: '1' - }, - -]) +const fishTableData: any = ref([]) const fishDialog = ref(false) const fishTableSelection = ref([]) //获取过鱼设施权限数据 -function getFishTableData() { - fishDialog.value = true - fishDialog.value = false; - // 获取过鱼设施权限数据 - // getFishPermissions().then((res: any) => { - // fishTableData.value = res.data - // }) +function getFishTableData(ids: any[]) { + + // 提取fishData.value中code在ids数组中的所有数据项 + const extractedData: any[] = []; + + function extractMatchingNodes(nodes: any[]) { + for (const node of nodes) { + if (ids.includes(node.code)) { + // 如果当前节点的code在ids中,则添加到结果数组 + extractedData.push({ + name: node.name, + type: node.type, // 默认类型为'站点' + id: node.code.toString(), // 使用code作为id + code: node.code, + path: node.path, + userId: userId.value, + parentId: node.parentId, + orgLevel: node.orgLevel, + permissionType: 'READ' // 默认选择读权限 + }); + } + + // 递归检查子节点 + if (node.children && node.children.length > 0) { + extractMatchingNodes(node.children); + } + } + } + + extractMatchingNodes(fishData.value); + fishTableData.value = extractedData; } //权限给予 -const tableids:any = ref([]) +const tableids: any = ref([]) //选中的过鱼权限 function fishDataHandleSelectionChange(val: any) { console.log('选中的行数据:', val); fishTableSelection.value = val } + //移除权限 function delFishTable() { ElMessageBox.confirm("确定移除选中权限吗?", "删除提示", { @@ -591,6 +580,27 @@ function delFishTable() { return !selectedIds.includes(item.id); }); + // 同步更新树形控件的选中状态 + if (fishTreeRef.value) { + // 获取树形数据,用于遍历子节点 + const treeData = fishData.value; + + // 收集所有需要取消选中的节点(包括选中的节点及其所有子节点) + const nodesToUncheck = new Set(); + + // 对于每个选中的ID,找到其在树中的节点及其所有子节点 + selectedIds.forEach((id: any) => { + nodesToUncheck.add(id); + // 查找该节点的所有子节点 + findAllChildNodes(treeData, id, nodesToUncheck); + }); + + // 将收集到的所有节点取消选中 + nodesToUncheck.forEach((nodeId: any) => { + fishTreeRef.value.setChecked(nodeId, false, false); + }); + } + ElMessage({ type: "success", message: "删除成功", @@ -603,15 +613,51 @@ function delFishTable() { // 用户取消删除 }); } + +// 递归查找指定节点的所有子节点 +function findAllChildNodes(nodes: any[], targetNodeId: any, result: Set) { + for (const node of nodes) { + if (node.code == targetNodeId) { + // 找到了目标节点,递归收集它的所有子节点 + if (node.children && node.children.length > 0) { + for (const child of node.children) { + result.add(child.code); + // 递归处理子节点的子节点 + findAllChildNodes([child], child.code, result); + } + } + break; // 找到目标节点后就跳出循环 + } else if (node.children && node.children.length > 0) { + // 继续在子节点中查找 + findAllChildNodes(node.children, targetNodeId, result); + } + } +} //权限确定 function fishSure() { - // 获取fishTableData中的所有ID - const ids = fishTableData.value.map((item: any) => item.id); - // userId.value //用户id - console.log('fishTableData中的所有ID:', ids); - - // 这里可以根据业务需求使用ids数组,比如调用接口保存权限 - // saveFishPermissions(ids); + let params: any = [] + fishTableData.value.forEach((item: any) => { + params.push({ + engName: item.name, + userId: item.userId, + orgType: item.type, + orgId: item.code, + parentId: item.parentId, + orgLevel: item.orgLevel, + path: item.path, + permissionType: item.permissionType, + }) + }) + saveFishqvan({ userId: userId.value, dataScopeList: params }).then((res: any) => { + console.log(res); + if (res.code == 0) { + ElMessage({ + message: '保存成功', + type: 'success', + }) + fishHandleClose() + } + }) } //删除用户 function delclick(row: any) { @@ -690,10 +736,26 @@ function dateFormat(row: any) { } //分页 +//分类名字 +function getName(arr: any[], type: any): string { + if (!arr || !Array.isArray(arr) || type === undefined || type === null) { + return '' + } + + const item = arr.find((item: any) => item?.itemCode === type) + return item?.dictName || '' +} onMounted(() => { getTree(); + getdictdata() }); +const dictData = ref([]) +function getdictdata(){ + getDictItemsByCode({dictCode:'resourceType'}).then(res=>{ + dictData.value = res.data + }) +} const vMove = { mounted(el: any) { el.onmousedown = function (e: any) { @@ -711,6 +773,114 @@ const vMove = { }; } } +// 递归获取树形数据中所有节点的code +function getAllNodeCodes(nodes: any[]): number[] { + const codes: number[] = []; + nodes.forEach((node: any) => { + codes.push(node.code); + if (node.children && node.children.length > 0) { + codes.push(...getAllNodeCodes(node.children)); + } + }); + return codes; +} + +// 全选:选中树中所有节点 +function handleSelectAll() { + if (!fishTreeRef.value || !fishData.value || fishData.value.length === 0) { + ElMessage.warning('暂无数据可全选'); + return; + } + const allCodes = getAllNodeCodes(fishData.value); + console.log('全选 - 所有节点codes:', allCodes); + fishTreeRef.value.setCheckedKeys(allCodes, true); + + // 手动更新表格数据(使用统一过滤逻辑) + setTimeout(() => { + // 获取当前选中的节点信息 + const checkedKeys = fishTreeRef.value.getCheckedKeys(); + const checkedNodes = fishTreeRef.value.getCheckedNodes(false, false); // 只获取完全选中的节点 + + // 使用统一的过滤逻辑 + const filteredIds = filterSelectedIds(checkedKeys, checkedNodes); + + getFishTableData(filteredIds); + tableids.value = filteredIds; + console.log('全选 - 表格数据已更新,过滤后IDs:', filteredIds); + }, 100); + + ElMessage.success('已全选所有节点'); +} + +// 反选:反转当前选中状态 +function handleInvertSelection() { + if (!fishTreeRef.value || !fishData.value || fishData.value.length === 0) { + ElMessage.warning('暂无数据可操作'); + return; + } + + // 获取当前完全选中的节点keys + const checkedKeys = fishTreeRef.value.getCheckedKeys(); + // 获取当前半选的节点keys(父节点部分子节点被选中) + const halfCheckedKeys = fishTreeRef.value.getHalfCheckedKeys(); + + console.log('反选前 - 已选中codes:', checkedKeys); + console.log('反选前 - 半选codes:', halfCheckedKeys); + + // 合并完全选中和半选的节点,这些是需要取消选中的 + const currentSelectedKeys = [...new Set([...checkedKeys, ...halfCheckedKeys])]; + console.log('反选前 - 当前有效选中状态:', currentSelectedKeys); + + // 获取所有节点codes + const allCodes = getAllNodeCodes(fishData.value); + + // 计算需要选中的节点:所有节点 - 当前有效选中状态 + const invertedCodes = allCodes.filter(code => !currentSelectedKeys.includes(code)); + console.log('反选后 - 新的选中codes:', invertedCodes); + + // 先清空所有选中状态,避免级联影响 + fishTreeRef.value.setCheckedKeys([], false); + + // 然后设置新的选中状态 + setTimeout(() => { + fishTreeRef.value.setCheckedKeys(invertedCodes, true); + + // 手动更新表格数据(使用统一过滤逻辑) + setTimeout(() => { + // 获取当前选中的节点信息 + const newCheckedKeys = fishTreeRef.value.getCheckedKeys(); + const newCheckedNodes = fishTreeRef.value.getCheckedNodes(false, false); + + // 使用统一的过滤逻辑 + const filteredIds = filterSelectedIds(newCheckedKeys, newCheckedNodes); + + getFishTableData(filteredIds); + tableids.value = filteredIds; + console.log('反选 - 表格数据已更新,过滤后IDs:', filteredIds); + }, 100); + }, 50); + + ElMessage.success('已反选操作'); +} + +// 取消选中:清空所有选中状态 +function handleClearSelection() { + if (!fishTreeRef.value) { + return; + } + console.log('取消选中 - 清空所有选中'); + fishTreeRef.value.setCheckedKeys([], true); + + // 手动清空表格数据 + setTimeout(() => { + getFishTableData([]); + tableids.value = []; + console.log('取消选中 - 表格数据已清空'); + }, 100); + + ElMessage.success('已取消所有选中'); +} + +
+ 全选 + 反选 + 取消选中 +
- + @@ -857,22 +1032,22 @@ const vMove = { 移除权限 - -