Files
cattleTransportation/pc-cattle-transportation/src/views/permission/menuPermission.vue
2025-10-23 17:28:06 +08:00

580 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="permission-container">
<el-row :gutter="20">
<!-- 左侧用户列表 -->
<el-col :span="8">
<el-card class="user-card">
<template #header>
<div class="card-header">
<span class="card-title">用户列表</span>
<el-tag type="success" size="small" style="margin-left: 10px;">
用户专属权限
</el-tag>
</div>
</template>
<el-table
:data="paginatedUserList"
highlight-current-row
@current-change="handleUserChange"
v-loading="userLoading"
style="width: 100%"
max-height="500"
>
<el-table-column prop="name" label="用户名称" width="120" />
<el-table-column prop="mobile" label="手机号" />
<el-table-column prop="roleId" label="角色ID" width="80" />
</el-table>
<!-- 分页器 -->
<div style="margin-top: 15px; text-align: center">
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="userList.length"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
</el-col>
<!-- 右侧菜单权限分配 -->
<el-col :span="16">
<el-card class="permission-card">
<template #header>
<div class="card-header">
<span class="card-title">
{{ currentUser ? `${currentUser.name} - 菜单访问权限` : '请选择用户' }}
</span>
<el-tag v-if="currentUser" type="success" size="small" style="margin-right: 10px">
用户ID: {{ currentUser.id }}
</el-tag>
<el-button
type="primary"
size="small"
@click="handleSaveMenuPermissions"
:disabled="!currentUser"
:loading="saveLoading"
>
保存菜单权限
</el-button>
<el-button
type="success"
size="small"
@click="handleQuickAssignAll"
:disabled="!currentUser"
:loading="quickAssignLoading"
style="margin-left: 10px"
>
一键分配全部菜单权限
</el-button>
<el-button
type="danger"
size="small"
@click="handleClearUserPermissions"
:disabled="!currentUser"
:loading="clearLoading"
style="margin-left: 10px"
>
清空用户权限
</el-button>
</div>
</template>
<div v-if="currentUser" v-loading="permissionLoading">
<el-alert
title="用户专属菜单权限管理"
type="info"
:closable="false"
style="margin-bottom: 20px"
>
<template #default>
<div>
<p><strong>当前系统使用基于用户的菜单权限管理</strong></p>
<p> 修改菜单权限只影响当前选择的用户</p>
<p> 当前用户: <strong>{{ currentUser.name }}</strong> (ID: {{ currentUser.id }})</p>
<p> 角色ID: <strong>{{ currentUser.roleId }}</strong></p>
<p> 勾选菜单后该用户可以访问相应的菜单页面</p>
<p> 按钮权限请在"操作权限管理"页面中设置</p>
</div>
</template>
</el-alert>
<el-tree
ref="menuTreeRef"
:data="menuTree"
show-checkbox
node-key="id"
:default-expand-all="true"
:props="treeProps"
:check-strictly="false"
:default-checked-keys="checkedMenuIds"
>
<template #default="{ node, data }">
<span class="tree-node">
<el-icon v-if="data.icon" style="margin-right: 5px">
<component :is="data.icon" />
</el-icon>
<span>{{ node.label }}</span>
<el-tag
v-if="data.type === 1"
type="success"
size="small"
style="margin-left: 10px"
>
菜单
</el-tag>
<el-tag
v-if="data.type === 2"
type="warning"
size="small"
style="margin-left: 10px"
>
按钮
</el-tag>
<span
v-if="data.authority"
style="margin-left: 10px; color: #999; font-size: 12px"
>
{{ data.authority }}
</span>
</span>
</template>
</el-tree>
</div>
<el-empty
v-else
description="请从左侧选择一个角色,为其分配菜单访问权限"
:image-size="100"
/>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick, computed } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import {
getUserList,
getMenuTree,
getUserMenuIds,
assignUserMenus,
clearUserMenus,
getMenuList,
} from '@/api/permission.js';
import usePermissionStore from '@/store/permission.js';
// 用户相关数据
const userLoading = ref(false);
const userList = ref([]);
const currentUser = ref(null);
// 分页数据
const pagination = reactive({
currentPage: 1,
pageSize: 10,
});
// 计算分页后的用户列表
const paginatedUserList = computed(() => {
const start = (pagination.currentPage - 1) * pagination.pageSize;
const end = start + pagination.pageSize;
return userList.value.slice(start, end);
});
// 权限相关数据
const permissionLoading = ref(false);
const saveLoading = ref(false);
const quickAssignLoading = ref(false);
const clearLoading = ref(false);
const menuTree = ref([]);
const menuTreeRef = ref(null);
const checkedMenuIds = ref([]);
const treeProps = {
children: 'children',
label: 'label',
};
// 初始化
onMounted(() => {
loadUserList();
});
// 加载用户列表
const loadUserList = async () => {
userLoading.value = true;
try {
const res = await getUserList();
if (res.code === 200) {
userList.value = res.data || [];
}
} catch (error) {
console.error('加载用户列表失败:', error);
ElMessage.error('加载用户列表失败');
} finally {
userLoading.value = false;
}
};
// 用户选择改变
const handleUserChange = async (row) => {
if (!row) return;
console.log('=== 菜单权限管理 - 用户选择改变 ===');
console.log('选择的用户:', row);
console.log('用户ID:', row.id);
currentUser.value = row;
await loadMenuTree();
await loadUserMenus(row.id);
};
// 加载菜单树(只显示菜单,不显示按钮)
const loadMenuTree = async () => {
permissionLoading.value = true;
try {
const res = await getMenuTree();
if (res.code === 200) {
// 过滤掉按钮权限type=2只保留菜单type=0,1
const filteredTree = filterMenuTree(res.data || []);
menuTree.value = filteredTree;
console.log('=== 菜单权限管理 - 过滤后的菜单树 ===', filteredTree);
}
} catch (error) {
console.error('加载菜单树失败:', error);
ElMessage.error('加载菜单树失败');
} finally {
permissionLoading.value = false;
}
};
// 过滤菜单树只保留菜单项type=0,1移除按钮type=2
const filterMenuTree = (tree) => {
return tree.map(node => {
const filteredNode = { ...node };
// 如果当前节点是按钮type=2返回null会被过滤掉
if (node.type === 2) {
return null;
}
// 如果有子节点,递归过滤
if (node.children && node.children.length > 0) {
const filteredChildren = filterMenuTree(node.children).filter(child => child !== null);
filteredNode.children = filteredChildren;
}
return filteredNode;
}).filter(node => node !== null);
};
// 加载用户已分配的菜单(只加载菜单权限,不加载按钮权限)
const loadUserMenus = async (userId) => {
try {
const res = await getUserMenuIds(userId);
if (res.code === 200) {
const allMenuIds = res.data || [];
// 过滤掉按钮权限,只保留菜单权限
const menuOnlyIds = await filterMenuOnlyIds(allMenuIds);
checkedMenuIds.value = menuOnlyIds;
console.log('=== 菜单权限管理 - 过滤后的菜单权限 ===', {
userId: userId,
allMenuIds: allMenuIds,
menuOnlyIds: menuOnlyIds
});
await nextTick();
if (menuTreeRef.value) {
menuTreeRef.value.setCheckedKeys(checkedMenuIds.value);
}
}
} catch (error) {
console.error('加载用户菜单失败:', error);
ElMessage.error('加载用户菜单失败');
}
};
// 过滤菜单ID列表只保留菜单项type=0,1移除按钮type=2
const filterMenuOnlyIds = async (menuIds) => {
try {
// 获取所有菜单信息
const menuListRes = await getMenuList();
if (menuListRes.code !== 200) {
return menuIds; // 如果获取失败,返回原始列表
}
const allMenus = menuListRes.data || [];
const menuMap = new Map(allMenus.map(menu => [menu.id, menu]));
// 过滤掉按钮权限
const menuOnlyIds = menuIds.filter(id => {
const menu = menuMap.get(id);
return menu && menu.type !== 2; // 只保留菜单项type=0,1
});
return menuOnlyIds;
} catch (error) {
console.error('过滤菜单权限失败:', error);
return menuIds; // 如果过滤失败,返回原始列表
}
};
// 分页处理
const handleSizeChange = (size) => {
pagination.pageSize = size;
pagination.currentPage = 1;
};
const handleCurrentChange = (page) => {
pagination.currentPage = page;
};
// 保存菜单权限(只保存菜单权限,不保存按钮权限)
const handleSaveMenuPermissions = async () => {
if (!currentUser.value) {
ElMessage.warning('请先选择用户');
return;
}
// 确认对话框,让用户明确知道影响范围
try {
await ElMessageBox.confirm(
`您即将为用户 ${currentUser.value.name} (ID: ${currentUser.value.id}) 设置菜单权限。\n\n这将只影响当前选择的用户。\n\n确定要继续吗`,
'确认菜单权限修改',
{
confirmButtonText: '确定修改',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: false
}
);
} catch {
// 用户取消操作
return;
}
// 获取选中的节点(包括半选中的父节点)
const checkedKeys = menuTreeRef.value.getCheckedKeys();
const halfCheckedKeys = menuTreeRef.value.getHalfCheckedKeys();
const allKeys = [...checkedKeys, ...halfCheckedKeys];
// 过滤掉按钮权限,只保留菜单权限
const menuOnlyIds = await filterMenuOnlyIds(allKeys);
console.log('=== 保存菜单权限 ===', {
user: currentUser.value,
allKeys: allKeys,
menuOnlyIds: menuOnlyIds
});
saveLoading.value = true;
try {
const res = await assignUserMenus({
userId: currentUser.value.id,
menuIds: menuOnlyIds, // 只保存菜单权限
});
if (res.code === 200) {
ElMessage.success(`用户 ${currentUser.value.name} 的菜单权限保存成功,共保存 ${menuOnlyIds.length} 个菜单权限。`);
// 权限保存成功后,刷新权限数据
try {
const permissionStore = usePermissionStore();
await permissionStore.refreshPermissions();
ElMessage.success('权限已保存并刷新成功!');
console.log('权限数据已刷新');
} catch (error) {
console.error('刷新权限失败:', error);
ElMessage.warning('权限已保存,但刷新失败,请手动刷新页面');
}
} else {
ElMessage.error(res.msg || '保存失败');
}
} catch (error) {
console.error('保存菜单权限失败:', error);
ElMessage.error('保存失败');
} finally {
saveLoading.value = false;
}
};
// 一键分配全部菜单权限(只分配菜单权限,不分配按钮权限)
const handleQuickAssignAll = async () => {
if (!currentUser.value) {
ElMessage.warning('请先选择用户');
return;
}
try {
// 确认操作
const confirmed = await ElMessageBox.confirm(
`您即将为用户 ${currentUser.value.name} (ID: ${currentUser.value.id}) 分配所有菜单权限。\n\n这将只影响当前选择的用户。\n\n注意此操作只分配菜单权限按钮权限请在"操作权限管理"页面中设置。`,
'确认分配菜单权限',
{
confirmButtonText: '确定分配',
cancelButtonText: '取消',
type: 'warning',
}
);
if (!confirmed) {
return;
}
quickAssignLoading.value = true;
// 获取所有菜单
const menuListRes = await getMenuList();
if (menuListRes.code !== 200) {
throw new Error('获取菜单列表失败');
}
const allMenus = menuListRes.data || [];
// 过滤掉按钮权限,只保留菜单权限
const menuOnlyMenus = allMenus.filter(menu => menu.type !== 2);
const menuOnlyIds = menuOnlyMenus.map(menu => menu.id);
console.log('=== 一键分配全部菜单权限 ===', {
user: currentUser.value,
totalMenus: allMenus.length,
menuOnlyMenus: menuOnlyMenus.length,
menuOnlyIds: menuOnlyIds
});
// 分配所有菜单权限
const res = await assignUserMenus({
userId: currentUser.value.id,
menuIds: menuOnlyIds,
});
if (res.code === 200) {
ElMessage.success(`成功为用户 ${currentUser.value.name} 分配了 ${menuOnlyIds.length} 个菜单权限。`);
// 重新加载权限显示
await loadUserMenus(currentUser.value.id);
// 权限保存成功后,刷新权限数据
try {
const permissionStore = usePermissionStore();
await permissionStore.refreshPermissions();
ElMessage.success('权限已保存并刷新成功!');
console.log('权限数据已刷新');
} catch (error) {
console.error('刷新权限失败:', error);
ElMessage.warning('权限已保存,但刷新失败,请手动刷新页面');
}
} else {
ElMessage.error(res.msg || '分配失败');
}
} catch (error) {
if (error !== 'cancel') {
console.error('一键分配菜单权限失败:', error);
ElMessage.error(`分配失败: ${error.message || error}`);
}
} finally {
quickAssignLoading.value = false;
}
};
// 清空用户权限
const handleClearUserPermissions = async () => {
if (!currentUser.value) {
ElMessage.warning('请先选择用户');
return;
}
try {
// 确认操作
const confirmed = await ElMessageBox.confirm(
`您即将清空用户 ${currentUser.value.name} (ID: ${currentUser.value.id}) 的所有权限。\n\n清空后该用户将无法访问任何菜单。\n\n确定要继续吗`,
'确认清空用户权限',
{
confirmButtonText: '确定清空',
cancelButtonText: '取消',
type: 'warning',
}
);
if (!confirmed) {
return;
}
clearLoading.value = true;
// 清空用户权限
const res = await clearUserMenus(currentUser.value.id);
if (res.code === 200) {
ElMessage.success(`用户 ${currentUser.value.name} 的权限已清空!`);
// 重新加载权限显示
await loadUserMenus(currentUser.value.id);
// 权限清空后,刷新权限数据
try {
const permissionStore = usePermissionStore();
await permissionStore.refreshPermissions();
ElMessage.success('权限已清空并刷新成功!');
console.log('权限数据已刷新');
} catch (error) {
console.error('刷新权限失败:', error);
ElMessage.warning('权限已清空,但刷新失败,请手动刷新页面');
}
} else {
ElMessage.error(res.msg || '清空失败');
}
} catch (error) {
if (error !== 'cancel') {
console.error('清空用户权限失败:', error);
ElMessage.error(`清空失败: ${error.message || error}`);
}
} finally {
clearLoading.value = false;
}
};
</script>
<style scoped>
.permission-container {
padding: 20px;
}
.user-card,
.permission-card {
border-radius: 8px;
min-height: 600px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-size: 16px;
font-weight: bold;
}
.tree-node {
display: flex;
align-items: center;
flex: 1;
}
:deep(.el-tree-node__content) {
height: 36px;
}
</style>