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