feat(角色管理): 添加数据权限控制功能
- 新增数据权限字段并与菜单权限联动 - 重构菜单权限界面为左右布局 - 添加数据权限与菜单权限的双向绑定逻辑 - 优化菜单选项数据结构,增加数据权限字段
This commit is contained in:
parent
a2a615d18d
commit
977615baed
|
@ -34,6 +34,8 @@ const {
|
|||
|
||||
const opType = ref<"add" | "update">("add");
|
||||
const opRow = ref<RoleDTO>();
|
||||
const dataScope = ref<Record<number, string>>({});
|
||||
(window as any).dataScope = dataScope;
|
||||
|
||||
const formData = reactive<AddRoleCommand | UpdateRoleCommand>({
|
||||
roleId: 0,
|
||||
|
@ -87,6 +89,7 @@ watch(activeTab, async (val) => {
|
|||
await getRoleInfo("update", opRow.value);
|
||||
Object.assign(formData, opRow.value);
|
||||
formData.menuIds = opRow.value.selectedMenuList;
|
||||
updateDataScope();
|
||||
});
|
||||
async function getRoleInfo(type: "add" | "update", row?: RoleDTO) {
|
||||
try {
|
||||
|
@ -103,7 +106,8 @@ async function getRoleInfo(type: "add" | "update", row?: RoleDTO) {
|
|||
opType.value = type;
|
||||
opRow.value = row;
|
||||
}
|
||||
const processedMenuOptions = ref<{ categoryName: string; items: MenuDTO[] }[]>([]);
|
||||
const processedMenuOptions = ref<{ id:number, categoryName: string; items: MenuDTO[]; dataScope: string }[]>([]);
|
||||
(window as any).processedMenuOptions = processedMenuOptions;
|
||||
|
||||
// 监听菜单树变化,处理菜单选项结构
|
||||
watch(menuTree, (val) => {
|
||||
|
@ -127,8 +131,9 @@ watch(menuTree, (val) => {
|
|||
// 如果有叶子节点,则创建分类
|
||||
if (leaves.length) {
|
||||
categories.push({
|
||||
categoryName: menuOption.menuName, // 使用菜单名作为分类名
|
||||
items: leaves // 包含当前菜单及其叶子节点
|
||||
id: menuOption.id,
|
||||
categoryName: menuOption.menuName,
|
||||
items: leaves
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -154,10 +159,45 @@ watch(menuTree, (val) => {
|
|||
processedMenuOptions.value = categories;
|
||||
});
|
||||
|
||||
// 监听菜单选择变化,更新数据权限
|
||||
const updateDataScope = () => {
|
||||
dataScope.value = {};
|
||||
processedMenuOptions.value.forEach(category => {
|
||||
const selectedItem = category.items.find(item => item.menuName == '读写' && formData.menuIds.includes(item.id));
|
||||
const permission = selectedItem ? '1' : '0';
|
||||
dataScope.value[category.id] = `${category.id}-${permission}`;
|
||||
});
|
||||
};
|
||||
|
||||
// 监听dataScope变化,反向更新menuIds
|
||||
watch(
|
||||
dataScope,
|
||||
(newDataScope) => {
|
||||
Object.entries(newDataScope).forEach(([categoryId, scopeValue]) => {
|
||||
const [id, permission] = scopeValue.split('-');
|
||||
const category = processedMenuOptions.value.find(cat => cat.id === Number(id));
|
||||
if (!category) return;
|
||||
|
||||
const writeReadItem = category.items.find(item => item.menuName === '读写');
|
||||
if (!writeReadItem) return;
|
||||
|
||||
if (permission === '1') {
|
||||
if (!formData.menuIds.includes(writeReadItem.id)) {
|
||||
formData.menuIds.push(writeReadItem.id);
|
||||
}
|
||||
} else {
|
||||
formData.menuIds = formData.menuIds.filter(id => id !== writeReadItem.id);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
async function handleConfirm() {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
loading.value = true;
|
||||
formData.dataScope = Object.values(dataScope.value).join(',');
|
||||
console.log("opType", opType.value);
|
||||
if (opType.value === 'add') {
|
||||
await addRoleApi(formData as AddRoleCommand);
|
||||
|
@ -216,27 +256,42 @@ onSearch().then(() => {
|
|||
statusList[item].label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item> -->
|
||||
<el-form-item prop="menuIds" class="form-input">
|
||||
<template #label>
|
||||
<div style="display: flex;">
|
||||
<div class="form-input-data">
|
||||
<div class="menu-container">
|
||||
<div class="menu-label">
|
||||
<span>菜单权限</span>
|
||||
<!-- <el-checkbox-group v-model="formData.menuIds" class="checkbox-group">
|
||||
<el-checkbox v-model="formData.menuIds" label="all" class="menu-checkbox">
|
||||
全部
|
||||
</el-checkbox>
|
||||
</el-checkbox-group> -->
|
||||
<label class="el-form-item__label">数据权限</label>
|
||||
</div>
|
||||
<template v-for="menu in processedMenuOptions" :key="menu.categoryName">
|
||||
<div v-if="menu.items.some(item => item.menuName === '读写')">
|
||||
<el-radio-group v-model="dataScope[menu.id]">
|
||||
<el-radio :key="`${menu.id}-0`" :label="`${menu.id}-0`">只读</el-radio>
|
||||
<el-radio :key="`${menu.id}-1`" :label="`${menu.id}-1`">读写</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div v-else class="empty-placeholder"></div>
|
||||
<div class="empty-box"></div>
|
||||
<el-divider class="divider" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div prop="menuIds" class="form-input" style="flex: 1;">
|
||||
<div class="menu-container">
|
||||
<div class="menu-label">
|
||||
<label class="el-form-item__label">菜单权限</label>
|
||||
</div>
|
||||
<el-checkbox-group v-model="formData.menuIds" class="checkbox-group">
|
||||
<template v-for="menu in processedMenuOptions" :key="menu.categoryName">
|
||||
<!-- <span class="menu-category">{{ menu.categoryName }}:</span> -->
|
||||
<el-checkbox v-for="item in menu.items" :key="item.id" :label="item.id" class="menu-checkbox">
|
||||
<el-checkbox v-for="item in menu.items.filter(item => item.menuName != '读写')" :key="item.id" :label="item.id" class="menu-checkbox">
|
||||
|
||||
{{ item.menuName }}
|
||||
</el-checkbox>
|
||||
<el-divider class="divider" />
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <el-form-item prop="remark" label="备注" style="margin-bottom: 0">
|
||||
<el-input type="textarea" class="form-input" v-model="formData.remark" />
|
||||
</el-form-item> -->
|
||||
|
@ -303,6 +358,13 @@ onSearch().then(() => {
|
|||
flex: 1;
|
||||
}
|
||||
|
||||
.form-input-data {
|
||||
margin-left: 50px;
|
||||
|
||||
.el-radio {
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
.form-input {
|
||||
width: 40%;
|
||||
}
|
||||
|
@ -316,4 +378,17 @@ onSearch().then(() => {
|
|||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
.empty-placeholder {
|
||||
height: 32px;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue