This commit is contained in:
lvjunzhao
2025-02-10 16:36:59 +08:00
5 changed files with 274 additions and 44 deletions

View File

@ -24,6 +24,7 @@ enum Api {
Desktop = '/desktop/relation',
AppMenu = '/app/menu/simple-tree',
AppMenuAuth = '/organization/role/app-auth',
LogoutRole = '/system/authorize/logoutRole',
}
/**
@ -371,3 +372,23 @@ export async function getRoleMulti(ids: String, mode: ErrorMessageMode = 'modal'
},
);
}
/**
* @description: 角色接口登出授权
*/
export async function logoutByRoleId(
id: string,
mode: ErrorMessageMode = 'modal',
) {
return defHttp.get<number>(
{
url: Api.LogoutRole,
params: {
id,
},
},
{
errorMessageMode: mode,
},
);
}

View File

@ -46,6 +46,7 @@ export interface RoleUserModel {
export interface RoleSetAuthParams {
id: string;
type: number;
menuIds: string[]; //菜单ids
buttonIds: string[]; //按钮ids
columnIds: string[];

View File

@ -2,7 +2,7 @@
<PageWrapper dense fixedHeight contentFullHeight>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" v-auth="'index:add'" @click="handleCreate">
<a-button type="primary" v-auth="'dataAuth:add'" @click="handleCreate">
{{ t('新增') }}
</a-button>
</template>
@ -20,12 +20,12 @@
:actions="[
{
icon: 'clarity:note-edit-line',
auth: 'index:edit',
auth: 'dataAuth:edit',
onClick: handleEdit.bind(null, record),
},
{
icon: 'ant-design:delete-outlined',
auth: 'index:delete',
auth: 'dataAuth:delete',
color: 'error',
popConfirm: {
title: t('是否确认删除'),

View File

@ -6,13 +6,13 @@
:title="t('功能授权')"
destroy-on-close
@ok="handleSubmit"
width="90%"
:width=modalLength()
>
<template #title>
<div>功能授权<span style="color:red">注意修改角色权限会导致该角色所有用户自动登出请谨慎操作</span></div>
<div>功能授权<span style="color:red">注意修改角色权限后需要手动点击登出角色用户按钮或用户重新登录后才会生效</span></div>
</template>
<a-row :gutter="[16, 16]" class="h-full">
<a-col :span="6">
<a-col :span=spanLength() v-if="typeKey==4||typeKey==0">
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
t('系统功能')
}}</div>
@ -31,7 +31,7 @@
</BasicTree>
</div>
</a-col>
<a-col :span="6">
<a-col :span=spanLength() v-if="typeKey==4||typeKey==1">
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
t('按钮权限')
}}</div>
@ -45,7 +45,7 @@
/>
</div>
</a-col>
<a-col :span="6">
<a-col :span=spanLength() v-if="typeKey==4||typeKey==2">
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
t('字段权限')
}}</div>
@ -59,7 +59,7 @@
/>
</div>
</a-col>
<a-col :span="6">
<a-col :span=spanLength() v-if="typeKey==4||typeKey==3">
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
t('表单权限')
}}</div>
@ -117,6 +117,7 @@
const fieldFilterKeys = ref<string[]>([]);
const rowId = ref('');
const typeKey = ref(4);
function getTree(tree) {
if (!tree) {
@ -125,9 +126,26 @@
return tree;
}
function spanLength() {
if (typeKey.value == 4) {
return "6";
} else {
return "24";
}
}
function modalLength() {
if (typeKey.value == 4) {
return "90%";
} else {
return "40%";
}
}
const [registerRoleAuthModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false });
rowId.value = data.id;
typeKey.value = data.key;
treeData.value = await getMenuSimpleTree();
buttonData.value = await getMenuButtonList();
@ -143,37 +161,123 @@
let btnCheckedKey = [];
let colCheckedKey = [];
let fieldCheckedKey = [];
menuKeys.value.forEach((o) => {
getParentKeys(menuCheckedKey, treeData.value, o);
});
menuKeys.value = menuCheckedKey;
getTree(unref(treeRef)).setCheckedKeys(authList.menuIds);
initTree(menuCheckedKey);
if (typeKey.value == 4) {
menuKeys.value.forEach((o) => {
getParentKeys(menuCheckedKey, treeData.value, o);
});
menuKeys.value = menuCheckedKey;
getTree(unref(treeRef)).setCheckedKeys(authList.menuIds);
initTree(menuCheckedKey);
btnKeys.value.forEach((o) => {
getParentKeys(btnCheckedKey, buttonSelectData.value, o);
});
btnKeys.value = btnCheckedKey;
colKeys.value.forEach((o) => {
getParentKeys(colCheckedKey, columnSelectData.value, o);
});
colKeys.value = colCheckedKey;
let lostBtnKey = [];
btnKeys.value.forEach((o) => {
// getParentKeys(btnCheckedKey, buttonSelectData.value, o, lostBtnKey);
getParentKeysAndLost(btnCheckedKey, buttonSelectData.value, o, lostBtnKey);
});
if (lostBtnKey) {
let menuIds = [];
buttonData.value.forEach((but) => {
if (lostBtnKey.includes(but.id)) {
menuIds.push(but.menuId);
}
});
menuIds.forEach((key) => {
getParentKeys(btnCheckedKey, treeData.value, key);
});
findMenuTree(buttonSelectData.value, treeData.value, btnCheckedKey, 'button');
}
btnKeys.value = btnCheckedKey;
let lostColKey = [];
colKeys.value.forEach((o) => {
// getParentKeys(colCheckedKey, columnSelectData.value, o);
getParentKeysAndLost(colCheckedKey, columnSelectData.value, o, lostColKey);
});
if (lostColKey) {
let menuIds = [];
columnData.value.forEach((but) => {
if (lostColKey.includes(but.id)) {
menuIds.push(but.menuId);
}
});
menuIds.forEach((key) => {
getParentKeys(colCheckedKey, treeData.value, key);
});
findMenuTree(columnSelectData.value, treeData.value, colCheckedKey, 'column');
}
colKeys.value = colCheckedKey;
fieldKeys.value.forEach((o) => {
getParentKeys(fieldCheckedKey, fieldSelectData.value, o);
});
fieldKeys.value = fieldCheckedKey;
let lostFieldKey = [];
fieldKeys.value.forEach((o) => {
// getParentKeys(fieldCheckedKey, fieldSelectData.value, o);
getParentKeysAndLost(fieldCheckedKey, fieldSelectData.value, o, lostFieldKey);
});
if (lostFieldKey) {
let menuIds = [];
columnData.value.forEach((but) => {
if (lostFieldKey.includes(but.id)) {
menuIds.push(but.menuId);
}
});
menuIds.forEach((key) => {
getParentKeys(fieldCheckedKey, treeData.value, key);
});
findMenuTree(fieldSelectData.value, treeData.value, fieldCheckedKey, 'field');
}
fieldKeys.value = fieldCheckedKey;
nextTick(() => {
getTree(unref(ButtonRef))?.setCheckedKeys(authList.buttonIds);
getTree(unref(ColumnRef))?.setCheckedKeys(authList.columnIds);
getTree(unref(FieldRef))?.setCheckedKeys(authList.formIds);
getTree(unref(ButtonRef)).expandAll(true);
getTree(unref(ColumnRef)).expandAll(true);
getTree(unref(FieldRef)).expandAll(true);
});
nextTick(() => {
getTree(unref(ButtonRef))?.setCheckedKeys(authList.buttonIds);
getTree(unref(ColumnRef))?.setCheckedKeys(authList.columnIds);
getTree(unref(FieldRef))?.setCheckedKeys(authList.formIds);
getTree(unref(ButtonRef)).expandAll(true);
getTree(unref(ColumnRef)).expandAll(true);
getTree(unref(FieldRef)).expandAll(true);
});
getTree(unref(treeRef)).expandAll(true);
getTree(unref(treeRef)).expandAll(true);
} else {
if (typeKey.value == 0) {
menuKeys.value.forEach((o) => {
getParentKeys(menuCheckedKey, treeData.value, o);
});
menuKeys.value = menuCheckedKey;
getTree(unref(treeRef)).setCheckedKeys(authList.menuIds);
getTree(unref(treeRef)).expandAll(true);
}
const fullMenuKey = [];
getFullMenu(fullMenuKey, treeData.value);
initTree(fullMenuKey);
if (typeKey.value == 1) {
btnKeys.value.forEach((o) => {
getParentKeys(btnCheckedKey, buttonData.value, o);
});
btnKeys.value = btnCheckedKey;
getTree(unref(ButtonRef))?.setCheckedKeys(authList.buttonIds);
getTree(unref(ButtonRef)).expandAll(true);
}
else if (typeKey.value == 2) {
colKeys.value.forEach((o) => {
getParentKeys(colCheckedKey, columnData.value, o);
});
colKeys.value = colCheckedKey;
nextTick(() => {
getTree(unref(ColumnRef))?.setCheckedKeys(authList.columnIds);
getTree(unref(ColumnRef)).expandAll(true);
});
}
else if (typeKey.value == 3) {
fieldKeys.value.forEach((o) => {
getParentKeys(fieldCheckedKey, fieldData.value, o);
});
fieldKeys.value = fieldCheckedKey;
nextTick(() => {
getTree(unref(FieldRef))?.setCheckedKeys(authList.formIds);
getTree(unref(FieldRef)).expandAll(true);
});
}
}
});
async function handleSubmit() {
@ -197,6 +301,7 @@
await RoleSetAuth({
id: rowId.value,
type: typeKey.value == 4?NaN:typeKey.value,
menuIds: menuKeys.value,
buttonIds: btnKeys.value,
columnIds: colKeys.value,
@ -230,7 +335,8 @@
const menuSelect: string[] = [...e.halfCheckedKeys, ...keys];
initTree(menuSelect);
//选择菜单的时候。对应后面的button column field 只进行增量添加
incrementTree(menuSelect);
menuKeys.value = menuSelect;
}
@ -251,6 +357,20 @@
getTree(unref(ColumnRef))?.setExpandedKeys(menuSelect);
getTree(unref(FieldRef))?.setExpandedKeys(menuSelect);
}
function incrementTree(addMenuSelect) {
btnFilterKeys.value = [];
colFilterKeys.value = [];
fieldFilterKeys.value = [];
findMenuTree(buttonSelectData.value, treeData.value, addMenuSelect, 'button');
findMenuTree(columnSelectData.value, treeData.value, addMenuSelect, 'column');
findMenuTree(fieldSelectData.value, treeData.value, addMenuSelect, 'field');
let newBtnSelect = [...new Set([...btnKeys.value, ...addMenuSelect])];
getTree(unref(ButtonRef))?.setExpandedKeys(newBtnSelect);
getTree(unref(ColumnRef))?.setExpandedKeys([...new Set([...colKeys.value, ...addMenuSelect])]);
getTree(unref(FieldRef))?.setExpandedKeys([...new Set([...fieldKeys.value, ...addMenuSelect])]);
}
function getParentKeys(checkedKey, arr, keys) {
for (let i = 0; i < arr.length; i++) {
let o = arr[i];
@ -265,12 +385,43 @@
}
}
}
// keys获取对应的菜单 返回给chackedKey如果该keys 不在原先过滤后的数组arr中的话把keys 记录lostKey抛出给外面二次处理减少全数据过滤的内存处理
function getParentKeysAndLost(checkedKey, arr, keys, lostKey) {
for (let i = 0; i < arr.length; i++) {
let o = arr[i];
if (o.id == keys) {
checkedKey.push(o.id);
if (o.parentId > 0 && !checkedKey.includes(o.parentId)) {
checkedKey.push(o.parentId);
}
}
if (o.children?.length > 0) {
getParentKeys(checkedKey, o.children, keys);
}
}
if (!checkedKey.includes(keys)) {
lostKey.push(keys);
}
}
function findMenuTree(menuSelectTree, treeData, keys, type) {
for (let i = 0; i < treeData.length; i++) {
let o = cloneDeep(treeData[i]);
if (keys.includes(o.id)) {
o.children = [];
menuSelectTree.push(o);
let hadData = false;
let index = 0;
for (let j = 0; j < menuSelectTree.length; j++) {
if (menuSelectTree[j].id == o.id) {
hadData = true;
index = j;
break;
}
}
if (!hadData) {
o.children = [];
menuSelectTree.push(o);
} else {
o = menuSelectTree[index];
}
if (treeData[i].children?.length > 0) {
findMenuTree(o.children, treeData[i].children, keys, type);
} else {
@ -285,6 +436,15 @@
}
}
}
function getFullMenu(menuKeys, treeData) {
for (let i = 0; i < treeData.length; i++) {
let o = cloneDeep(treeData[i]);
menuKeys.push(o.id);
if (treeData[i].children?.length > 0) {
getFullMenu(menuKeys, treeData[i].children)
}
}
}
function getAuthData(list, id, type) {
let arr: TreeItem[] = [];
@ -375,6 +535,9 @@
columnSelectData,
fieldSelectData,
t,
typeKey,
spanLength,
modalLength,
};
},
});

View File

@ -13,9 +13,24 @@
<a-button type="primary" v-auth="'role:view'" @click="handleViewUser">
{{ t('查看成员') }}
</a-button>
<a-button type="primary" v-auth="'role:functionalAuth'" @click="handleAuth">
<!-- <a-button type="primary" v-auth="'role:functionalAuth'" @click="handleAuth">
{{ t('功能授权') }}
</a-button>
</a-button> -->
<a-dropdown v-auth="'role:functionalAuth'" >
<template #overlay>
<a-menu @click="handleAuth">
<a-menu-item key="4">全部</a-menu-item>
<a-menu-item key="0">菜单</a-menu-item>
<a-menu-item key="1">按钮</a-menu-item>
<a-menu-item key="2">字段</a-menu-item>
<a-menu-item key="3">表单</a-menu-item>
</a-menu>
</template>
<a-button type="primary" v-auth="'role:functionalAuth'" >
{{ t('功能授权') }}
<DownOutlined />
</a-button>
</a-dropdown>
<a-button type="primary" v-auth="'role:functionalAuth'" @click="handleAppAuth">
{{ t('移动端功能授权') }}
</a-button>
@ -25,6 +40,9 @@
<a-button type="primary" v-auth="'role:homeAuth'" @click="handleHomeAuth">
{{ t('首页授权') }}
</a-button>
<a-button type="primary" v-auth="'role:logoutRole'" @click="handleLogoutRole">
{{ t('登出角色用户') }}
</a-button>
</template>
<template #action="{ record }">
<TableAction
@ -87,6 +105,7 @@
addRoleUser,
getRoleUser,
addRoleInterface,
logoutByRoleId,
} from '/@/api/system/role';
import AuthModal from '../dataAuthority/components/AuthModal.vue';
import HomeModal from './components/HomeModal.vue';
@ -96,9 +115,10 @@
import AppAuthModal from './components/AppAuthModal.vue';
import { SelectDepartment } from '/@/components/SelectOrganizational/index';
import { useMessage } from '/@/hooks/web/useMessage';
import { Switch } from 'ant-design-vue';
import { Switch, Modal } from 'ant-design-vue';
import { PageWrapper } from '/@/components/Page';
import { useModal } from '/@/components/Modal';
import type { MenuProps } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
export const columns: BasicColumn[] = [
@ -255,14 +275,14 @@
id: res.id,
});
}
function handleAuth() {
const handleAuth: MenuProps['onClick'] = e => {
let res = warning(true);
if (!res) {
return;
}
openRoleUserModal(true, {
id: res.id,
key: e.key,
});
}
function handleAppAuth() {
@ -374,6 +394,30 @@
id: res.id,
});
}
function handleLogoutRole() {
let res = warning();
if (!res) {
return;
}
Modal.confirm({
title: '确认是否登出角色用户?',
content: '该角色所有在线用户将自动登出,以使他们重新登录让权限修改生效。请注意,该操作可能导致用户丢失他们正在修改的内容!',
okText: '是',
okType: 'danger',
cancelText: '否',
onOk() {
logoutByRoleId(res.id).then((_) => {
notification.info({
message: t('登出成功'),
description: t('成功'),
}); //提示消息
});
},
onCancel() {
console.log('Cancel');
},
});
}
return {
registerTable,
registerRoleModal,
@ -398,6 +442,7 @@
t,
registerAppAuthModal,
handleAppAuth,
handleLogoutRole,
};
},
});