添加新的需求

This commit is contained in:
xuqiuyun
2025-10-20 17:32:09 +08:00
parent 9979e00b47
commit 361d5ab1ae
247 changed files with 34249 additions and 1 deletions

View File

@@ -0,0 +1,379 @@
<template>
<div class="permission-container">
<el-row :gutter="20">
<!-- 左侧用户列表 -->
<el-col :span="8">
<el-card class="role-card">
<template #header>
<div class="card-header">
<span class="card-title">用户列表</span>
</div>
</template>
<el-table
:data="paginatedRoleList"
highlight-current-row
@current-change="handleRoleChange"
v-loading="roleLoading"
style="width: 100%"
max-height="500"
>
<el-table-column prop="name" label="用户名称" width="120" />
<el-table-column prop="mobile" label="手机号" />
</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="roleList.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">
{{ currentRole ? `${currentRole.name} - 菜单访问权限` : '请选择角色' }}
</span>
<el-button
type="primary"
size="small"
@click="handleSaveMenuPermissions"
:disabled="!currentRole"
:loading="saveLoading"
>
保存菜单权限
</el-button>
<el-button
type="success"
size="small"
@click="handleQuickAssignAll"
:disabled="!currentRole"
:loading="quickAssignLoading"
style="margin-left: 10px"
>
一键分配全部权限
</el-button>
</div>
</template>
<div v-if="currentRole" v-loading="permissionLoading">
<el-alert
title="提示"
type="info"
:closable="false"
style="margin-bottom: 20px"
>
勾选菜单和按钮后该用户登录系统时可以访问这些菜单页面和执行相应的操作
</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,
getRoleMenuIds,
assignRoleMenus,
getMenuList,
} from '@/api/permission.js';
// 角色相关数据
const roleLoading = ref(false);
const roleList = ref([]);
const currentRole = ref(null);
// 分页数据
const pagination = reactive({
currentPage: 1,
pageSize: 10,
});
// 计算分页后的用户列表
const paginatedRoleList = computed(() => {
const start = (pagination.currentPage - 1) * pagination.pageSize;
const end = start + pagination.pageSize;
return roleList.value.slice(start, end);
});
// 权限相关数据
const permissionLoading = ref(false);
const saveLoading = ref(false);
const quickAssignLoading = ref(false);
const menuTree = ref([]);
const menuTreeRef = ref(null);
const checkedMenuIds = ref([]);
const treeProps = {
children: 'children',
label: 'label',
};
// 初始化
onMounted(() => {
loadRoleList();
});
// 加载用户列表
const loadRoleList = async () => {
roleLoading.value = true;
try {
const res = await getUserList();
if (res.code === 200) {
roleList.value = res.data || [];
}
} catch (error) {
console.error('加载用户列表失败:', error);
ElMessage.error('加载用户列表失败');
} finally {
roleLoading.value = false;
}
};
// 角色选择改变
const handleRoleChange = async (row) => {
if (!row) return;
currentRole.value = row;
await loadMenuTree();
await loadRoleMenus(row.roleId);
};
// 加载菜单树(显示所有菜单,包括菜单和按钮)
const loadMenuTree = async () => {
permissionLoading.value = true;
try {
const res = await getMenuTree();
if (res.code === 200) {
// 显示所有菜单和按钮,不进行过滤
menuTree.value = res.data || [];
}
} catch (error) {
console.error('加载菜单树失败:', error);
ElMessage.error('加载菜单树失败');
} finally {
permissionLoading.value = false;
}
};
// 加载角色已分配的菜单
const loadRoleMenus = async (roleId) => {
try {
const res = await getRoleMenuIds(roleId);
if (res.code === 200) {
checkedMenuIds.value = res.data || [];
await nextTick();
if (menuTreeRef.value) {
menuTreeRef.value.setCheckedKeys(checkedMenuIds.value);
}
}
} catch (error) {
console.error('加载角色菜单失败:', error);
ElMessage.error('加载角色菜单失败');
}
};
// 分页处理
const handleSizeChange = (size) => {
pagination.pageSize = size;
pagination.currentPage = 1;
};
const handleCurrentChange = (page) => {
pagination.currentPage = page;
};
// 保存菜单权限
const handleSaveMenuPermissions = async () => {
if (!currentRole.value) {
ElMessage.warning('请先选择用户');
return;
}
// 获取选中的节点(包括半选中的父节点)
const checkedKeys = menuTreeRef.value.getCheckedKeys();
const halfCheckedKeys = menuTreeRef.value.getHalfCheckedKeys();
const allKeys = [...checkedKeys, ...halfCheckedKeys];
saveLoading.value = true;
try {
const res = await assignRoleMenus({
roleId: currentRole.value.roleId,
menuIds: allKeys,
});
if (res.code === 200) {
ElMessage.success('菜单权限保存成功');
} else {
ElMessage.error(res.msg || '保存失败');
}
} catch (error) {
console.error('保存菜单权限失败:', error);
ElMessage.error('保存失败');
} finally {
saveLoading.value = false;
}
};
// 一键分配全部权限
const handleQuickAssignAll = async () => {
if (!currentRole.value) {
ElMessage.warning('请先选择用户');
return;
}
try {
// 确认操作
const confirmed = await ElMessageBox.confirm(
`确定要为用户 ${currentRole.value.name} (${currentRole.value.mobile}) 分配所有菜单权限吗?`,
'确认分配权限',
{
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 allMenuIds = allMenus.map(menu => menu.id);
console.log('=== 一键分配全部权限 ===', {
user: currentRole.value,
totalMenus: allMenus.length,
menuIds: allMenuIds
});
// 分配所有菜单权限
const res = await assignRoleMenus({
roleId: currentRole.value.roleId,
menuIds: allMenuIds,
});
if (res.code === 200) {
ElMessage.success(`成功为用户 ${currentRole.value.name} 分配了 ${allMenuIds.length} 个菜单权限`);
// 重新加载权限显示
await loadRoleMenus(currentRole.value.roleId);
} else {
ElMessage.error(res.msg || '分配失败');
}
} catch (error) {
if (error !== 'cancel') {
console.error('一键分配权限失败:', error);
ElMessage.error(`分配失败: ${error.message || error}`);
}
} finally {
quickAssignLoading.value = false;
}
};
</script>
<style scoped>
.permission-container {
padding: 20px;
}
.role-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>

View File

@@ -0,0 +1,339 @@
<template>
<div class="operation-permission-container">
<el-row :gutter="20">
<!-- 左侧用户列表 -->
<el-col :span="8">
<el-card class="role-card">
<template #header>
<div class="card-header">
<span class="card-title">用户列表</span>
</div>
</template>
<el-table
:data="paginatedRoleList"
highlight-current-row
@current-change="handleRoleChange"
v-loading="roleLoading"
style="width: 100%"
max-height="500"
>
<el-table-column prop="name" label="用户名称" width="120" />
<el-table-column prop="mobile" label="手机号" />
</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="roleList.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">
{{ currentRole ? `${currentRole.name} - 操作权限分配` : '请选择角色' }}
</span>
<el-button
type="primary"
size="small"
v-hasPermi="['permission:operation:assign']"
@click="handleSavePermissions"
:disabled="!currentRole"
:loading="saveLoading"
>
保存操作权限
</el-button>
</div>
</template>
<div v-if="currentRole" v-loading="permissionLoading">
<el-alert
title="提示"
type="info"
:closable="false"
style="margin-bottom: 20px"
>
勾选操作权限后该角色可以执行相应的按钮操作新增编辑删除等
</el-alert>
<el-tree
ref="permissionTreeRef"
:data="permissionTree"
show-checkbox
node-key="id"
:default-expand-all="true"
:props="treeProps"
:check-strictly="true"
>
<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="info"
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 } from 'element-plus';
import {
getUserList,
getMenuTree,
getRoleMenuIds,
assignRoleMenus,
} from '@/api/permission.js';
// 用户相关数据
const roleLoading = ref(false);
const roleList = ref([]);
const currentRole = ref(null);
// 分页数据
const pagination = reactive({
currentPage: 1,
pageSize: 10,
});
// 计算分页后的用户列表
const paginatedRoleList = computed(() => {
const start = (pagination.currentPage - 1) * pagination.pageSize;
const end = start + pagination.pageSize;
return roleList.value.slice(start, end);
});
// 权限相关数据
const permissionLoading = ref(false);
const saveLoading = ref(false);
const permissionTree = ref([]);
const permissionTreeRef = ref(null);
const checkedPermissionIds = ref([]);
const treeProps = {
children: 'children',
label: 'label',
};
// 初始化
onMounted(() => {
loadRoleList();
});
// 加载用户列表
const loadRoleList = async () => {
roleLoading.value = true;
try {
const res = await getUserList();
if (res.code === 200) {
roleList.value = res.data || [];
}
} catch (error) {
console.error('加载用户列表失败:', error);
ElMessage.error('加载用户列表失败');
} finally {
roleLoading.value = false;
}
};
// 角色选择改变
const handleRoleChange = async (row) => {
if (!row) return;
console.log('=== 用户选择改变 ===');
console.log('选择的用户:', row);
console.log('用户roleId:', row.roleId);
currentRole.value = row;
await loadPermissionTree();
await loadRolePermissions(row.roleId);
};
// 分页处理
const handleSizeChange = (size) => {
pagination.pageSize = size;
pagination.currentPage = 1;
};
const handleCurrentChange = (page) => {
pagination.currentPage = page;
};
// 加载权限树(包含所有菜单和按钮)
const loadPermissionTree = async () => {
permissionLoading.value = true;
try {
const res = await getMenuTree();
if (res.code === 200) {
permissionTree.value = res.data;
}
} catch (error) {
console.error('加载权限树失败:', error);
ElMessage.error('加载权限树失败');
} finally {
permissionLoading.value = false;
}
};
// 加载角色已分配的权限
const loadRolePermissions = async (roleId) => {
console.log('=== 加载角色权限 ===');
console.log('roleId:', roleId);
try {
const res = await getRoleMenuIds(roleId);
console.log('权限API响应:', res);
if (res.code === 200) {
checkedPermissionIds.value = res.data || [];
console.log('已分配的权限IDs:', checkedPermissionIds.value);
await nextTick();
if (permissionTreeRef.value) {
permissionTreeRef.value.setCheckedKeys(checkedPermissionIds.value);
console.log('权限树已设置选中状态');
// 验证权限树的实际选中状态
setTimeout(() => {
const actualCheckedKeys = permissionTreeRef.value.getCheckedKeys();
const actualHalfCheckedKeys = permissionTreeRef.value.getHalfCheckedKeys();
console.log('=== 权限树实际选中状态验证 ===');
console.log('实际选中的权限IDs:', actualCheckedKeys);
console.log('实际半选中的权限IDs:', actualHalfCheckedKeys);
console.log('期望的权限IDs:', checkedPermissionIds.value);
console.log('选中状态是否一致:', JSON.stringify(actualCheckedKeys.sort()) === JSON.stringify(checkedPermissionIds.value.sort()));
}, 100);
}
} else {
console.error('权限API返回错误:', res);
}
} catch (error) {
console.error('加载角色权限失败:', error);
ElMessage.error('加载角色权限失败');
}
};
// 保存操作权限
const handleSavePermissions = async () => {
if (!currentRole.value) {
ElMessage.warning('请先选择用户');
return;
}
console.log('=== 保存操作权限 ===');
console.log('当前用户:', currentRole.value);
console.log('用户roleId:', currentRole.value.roleId);
// 获取选中的节点(包括半选中的父节点)
const checkedKeys = permissionTreeRef.value.getCheckedKeys();
const halfCheckedKeys = permissionTreeRef.value.getHalfCheckedKeys();
const allKeys = [...checkedKeys, ...halfCheckedKeys];
console.log('选中的权限IDs:', checkedKeys);
console.log('半选中的权限IDs:', halfCheckedKeys);
console.log('所有权限IDs:', allKeys);
const saveData = {
roleId: currentRole.value.roleId,
menuIds: allKeys,
};
console.log('保存数据:', saveData);
saveLoading.value = true;
try {
const res = await assignRoleMenus(saveData);
console.log('保存API响应:', res);
if (res.code === 200) {
ElMessage.success('操作权限保存成功');
console.log('权限保存成功');
} else {
ElMessage.error(res.msg || '保存失败');
console.error('权限保存失败:', res);
}
} catch (error) {
console.error('保存操作权限失败:', error);
ElMessage.error('保存失败');
} finally {
saveLoading.value = false;
}
};
</script>
<style scoped>
.operation-permission-container {
padding: 20px;
}
.role-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>