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', Desktop = '/desktop/relation',
AppMenu = '/app/menu/simple-tree', AppMenu = '/app/menu/simple-tree',
AppMenuAuth = '/organization/role/app-auth', 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 { export interface RoleSetAuthParams {
id: string; id: string;
type: number;
menuIds: string[]; //菜单ids menuIds: string[]; //菜单ids
buttonIds: string[]; //按钮ids buttonIds: string[]; //按钮ids
columnIds: string[]; columnIds: string[];

View File

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

View File

@ -6,13 +6,13 @@
:title="t('功能授权')" :title="t('功能授权')"
destroy-on-close destroy-on-close
@ok="handleSubmit" @ok="handleSubmit"
width="90%" :width=modalLength()
> >
<template #title> <template #title>
<div>功能授权<span style="color:red">注意修改角色权限会导致该角色所有用户自动登出请谨慎操作</span></div> <div>功能授权<span style="color:red">注意修改角色权限后需要手动点击登出角色用户按钮或用户重新登录后才会生效</span></div>
</template> </template>
<a-row :gutter="[16, 16]" class="h-full"> <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">{{ <div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
t('系统功能') t('系统功能')
}}</div> }}</div>
@ -31,7 +31,7 @@
</BasicTree> </BasicTree>
</div> </div>
</a-col> </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">{{ <div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
t('按钮权限') t('按钮权限')
}}</div> }}</div>
@ -45,7 +45,7 @@
/> />
</div> </div>
</a-col> </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">{{ <div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
t('字段权限') t('字段权限')
}}</div> }}</div>
@ -59,7 +59,7 @@
/> />
</div> </div>
</a-col> </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">{{ <div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
t('表单权限') t('表单权限')
}}</div> }}</div>
@ -117,6 +117,7 @@
const fieldFilterKeys = ref<string[]>([]); const fieldFilterKeys = ref<string[]>([]);
const rowId = ref(''); const rowId = ref('');
const typeKey = ref(4);
function getTree(tree) { function getTree(tree) {
if (!tree) { if (!tree) {
@ -125,9 +126,26 @@
return tree; 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) => { const [registerRoleAuthModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false }); setModalProps({ confirmLoading: false });
rowId.value = data.id; rowId.value = data.id;
typeKey.value = data.key;
treeData.value = await getMenuSimpleTree(); treeData.value = await getMenuSimpleTree();
buttonData.value = await getMenuButtonList(); buttonData.value = await getMenuButtonList();
@ -143,37 +161,123 @@
let btnCheckedKey = []; let btnCheckedKey = [];
let colCheckedKey = []; let colCheckedKey = [];
let fieldCheckedKey = []; let fieldCheckedKey = [];
menuKeys.value.forEach((o) => { if (typeKey.value == 4) {
getParentKeys(menuCheckedKey, treeData.value, o); menuKeys.value.forEach((o) => {
}); getParentKeys(menuCheckedKey, treeData.value, o);
menuKeys.value = menuCheckedKey; });
getTree(unref(treeRef)).setCheckedKeys(authList.menuIds); menuKeys.value = menuCheckedKey;
initTree(menuCheckedKey); getTree(unref(treeRef)).setCheckedKeys(authList.menuIds);
initTree(menuCheckedKey);
btnKeys.value.forEach((o) => { let lostBtnKey = [];
getParentKeys(btnCheckedKey, buttonSelectData.value, o); btnKeys.value.forEach((o) => {
}); // getParentKeys(btnCheckedKey, buttonSelectData.value, o, lostBtnKey);
btnKeys.value = btnCheckedKey; getParentKeysAndLost(btnCheckedKey, buttonSelectData.value, o, lostBtnKey);
colKeys.value.forEach((o) => { });
getParentKeys(colCheckedKey, columnSelectData.value, o); if (lostBtnKey) {
}); let menuIds = [];
colKeys.value = colCheckedKey; 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) => { let lostFieldKey = [];
getParentKeys(fieldCheckedKey, fieldSelectData.value, o); fieldKeys.value.forEach((o) => {
}); // getParentKeys(fieldCheckedKey, fieldSelectData.value, o);
fieldKeys.value = fieldCheckedKey; 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(() => { nextTick(() => {
getTree(unref(ButtonRef))?.setCheckedKeys(authList.buttonIds); getTree(unref(ButtonRef))?.setCheckedKeys(authList.buttonIds);
getTree(unref(ColumnRef))?.setCheckedKeys(authList.columnIds); getTree(unref(ColumnRef))?.setCheckedKeys(authList.columnIds);
getTree(unref(FieldRef))?.setCheckedKeys(authList.formIds); getTree(unref(FieldRef))?.setCheckedKeys(authList.formIds);
getTree(unref(ButtonRef)).expandAll(true); getTree(unref(ButtonRef)).expandAll(true);
getTree(unref(ColumnRef)).expandAll(true); getTree(unref(ColumnRef)).expandAll(true);
getTree(unref(FieldRef)).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() { async function handleSubmit() {
@ -197,6 +301,7 @@
await RoleSetAuth({ await RoleSetAuth({
id: rowId.value, id: rowId.value,
type: typeKey.value == 4?NaN:typeKey.value,
menuIds: menuKeys.value, menuIds: menuKeys.value,
buttonIds: btnKeys.value, buttonIds: btnKeys.value,
columnIds: colKeys.value, columnIds: colKeys.value,
@ -230,7 +335,8 @@
const menuSelect: string[] = [...e.halfCheckedKeys, ...keys]; const menuSelect: string[] = [...e.halfCheckedKeys, ...keys];
initTree(menuSelect); //选择菜单的时候。对应后面的button column field 只进行增量添加
incrementTree(menuSelect);
menuKeys.value = menuSelect; menuKeys.value = menuSelect;
} }
@ -251,6 +357,20 @@
getTree(unref(ColumnRef))?.setExpandedKeys(menuSelect); getTree(unref(ColumnRef))?.setExpandedKeys(menuSelect);
getTree(unref(FieldRef))?.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) { function getParentKeys(checkedKey, arr, keys) {
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
let o = arr[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) { function findMenuTree(menuSelectTree, treeData, keys, type) {
for (let i = 0; i < treeData.length; i++) { for (let i = 0; i < treeData.length; i++) {
let o = cloneDeep(treeData[i]); let o = cloneDeep(treeData[i]);
if (keys.includes(o.id)) { if (keys.includes(o.id)) {
o.children = []; let hadData = false;
menuSelectTree.push(o); 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) { if (treeData[i].children?.length > 0) {
findMenuTree(o.children, treeData[i].children, keys, type); findMenuTree(o.children, treeData[i].children, keys, type);
} else { } 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) { function getAuthData(list, id, type) {
let arr: TreeItem[] = []; let arr: TreeItem[] = [];
@ -375,6 +535,9 @@
columnSelectData, columnSelectData,
fieldSelectData, fieldSelectData,
t, t,
typeKey,
spanLength,
modalLength,
}; };
}, },
}); });

View File

@ -13,9 +13,24 @@
<a-button type="primary" v-auth="'role:view'" @click="handleViewUser"> <a-button type="primary" v-auth="'role:view'" @click="handleViewUser">
{{ t('查看成员') }} {{ t('查看成员') }}
</a-button> </a-button>
<a-button type="primary" v-auth="'role:functionalAuth'" @click="handleAuth"> <!-- <a-button type="primary" v-auth="'role:functionalAuth'" @click="handleAuth">
{{ t('功能授权') }} {{ 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"> <a-button type="primary" v-auth="'role:functionalAuth'" @click="handleAppAuth">
{{ t('移动端功能授权') }} {{ t('移动端功能授权') }}
</a-button> </a-button>
@ -25,6 +40,9 @@
<a-button type="primary" v-auth="'role:homeAuth'" @click="handleHomeAuth"> <a-button type="primary" v-auth="'role:homeAuth'" @click="handleHomeAuth">
{{ t('首页授权') }} {{ t('首页授权') }}
</a-button> </a-button>
<a-button type="primary" v-auth="'role:logoutRole'" @click="handleLogoutRole">
{{ t('登出角色用户') }}
</a-button>
</template> </template>
<template #action="{ record }"> <template #action="{ record }">
<TableAction <TableAction
@ -87,6 +105,7 @@
addRoleUser, addRoleUser,
getRoleUser, getRoleUser,
addRoleInterface, addRoleInterface,
logoutByRoleId,
} from '/@/api/system/role'; } from '/@/api/system/role';
import AuthModal from '../dataAuthority/components/AuthModal.vue'; import AuthModal from '../dataAuthority/components/AuthModal.vue';
import HomeModal from './components/HomeModal.vue'; import HomeModal from './components/HomeModal.vue';
@ -96,9 +115,10 @@
import AppAuthModal from './components/AppAuthModal.vue'; import AppAuthModal from './components/AppAuthModal.vue';
import { SelectDepartment } from '/@/components/SelectOrganizational/index'; import { SelectDepartment } from '/@/components/SelectOrganizational/index';
import { useMessage } from '/@/hooks/web/useMessage'; 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 { PageWrapper } from '/@/components/Page';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import type { MenuProps } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n(); const { t } = useI18n();
export const columns: BasicColumn[] = [ export const columns: BasicColumn[] = [
@ -255,14 +275,14 @@
id: res.id, id: res.id,
}); });
} }
function handleAuth() { const handleAuth: MenuProps['onClick'] = e => {
let res = warning(true); let res = warning(true);
if (!res) { if (!res) {
return; return;
} }
openRoleUserModal(true, { openRoleUserModal(true, {
id: res.id, id: res.id,
key: e.key,
}); });
} }
function handleAppAuth() { function handleAppAuth() {
@ -374,6 +394,30 @@
id: res.id, 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 { return {
registerTable, registerTable,
registerRoleModal, registerRoleModal,
@ -398,6 +442,7 @@
t, t,
registerAppAuthModal, registerAppAuthModal,
handleAppAuth, handleAppAuth,
handleLogoutRole,
}; };
}, },
}); });