mirror of https://github.com/halo-dev/halo-admin
feat: refining the logic of user roles (#749)
#### What type of PR is this? /kind improvement /milestone 2.0.1 #### What this PR does / why we need it: 完善用户角色的相关逻辑。适配 https://github.com/halo-dev/halo/pull/2865 1. 支持标识是否是系统保留角色。 2. 根据是否是系统保留角色,禁用修改和删除的操作。 3. 支持判断是否是超级管理员,如果是,默认勾选所有权限。 4. 优化 `包含 N 个权限` 文案的逻辑,超级管理员为 `包含所有权限`。 5. 优化 `基于此角色创建` 的逻辑,判断是否为超级管理员,如果是,需要设置所有角色模板到创建表单。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2844 #### Screenshots: <img width="1663" alt="image" src="https://user-images.githubusercontent.com/21301288/205965292-b8f8e556-e06b-422b-b0be-8d87a68f18be.png"> <img width="1661" alt="image" src="https://user-images.githubusercontent.com/21301288/205965333-1491c023-6726-4cdd-b970-d868a30f3296.png"> #### Special notes for your reviewer: 测试方式: 1. Halo 需要切换到 https://github.com/halo-dev/halo/pull/2865 分支。 2. 测试角色相关的所有功能。 #### Does this PR introduce a user-facing change? ```release-note 完善 Console 端用户角色的相关逻辑 ```pull/758/head
parent
581f7156f5
commit
c70b069753
|
@ -0,0 +1 @@
|
|||
export const SUPER_ROLE_NAME = "super-role";
|
|
@ -6,6 +6,7 @@ export enum pluginLabels {
|
|||
// role
|
||||
export enum roleLabels {
|
||||
TEMPLATE = "halo.run/role-template",
|
||||
SYSTEM_RESERVED = "rbac.authorization.halo.run/system-reserved",
|
||||
}
|
||||
|
||||
// post
|
||||
|
|
|
@ -8,17 +8,19 @@ import {
|
|||
VTabbar,
|
||||
VTag,
|
||||
VAvatar,
|
||||
VAlert,
|
||||
} from "@halo-dev/components";
|
||||
import { useRoute } from "vue-router";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import { pluginLabels } from "@/constants/labels";
|
||||
import { pluginLabels, roleLabels } from "@/constants/labels";
|
||||
import { rbacAnnotations } from "@/constants/annotations";
|
||||
import {
|
||||
useRoleForm,
|
||||
useRoleTemplateSelection,
|
||||
} from "@/modules/system/roles/composables/use-role";
|
||||
import { useUserFetch } from "@/modules/system/users/composables/use-user";
|
||||
import { SUPER_ROLE_NAME } from "@/constants/constants";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
|
@ -31,6 +33,28 @@ const { formState, saving, handleCreateOrUpdate } = useRoleForm();
|
|||
|
||||
const { users } = useUserFetch({ fetchOnMounted: false });
|
||||
|
||||
const isSystemReserved = computed(() => {
|
||||
return (
|
||||
formState.value.metadata.labels?.[roleLabels.SYSTEM_RESERVED] === "true"
|
||||
);
|
||||
});
|
||||
|
||||
const isSuperRole = computed(() => {
|
||||
return formState.value.metadata.name === SUPER_ROLE_NAME;
|
||||
});
|
||||
|
||||
const getRoleCountText = computed(() => {
|
||||
if (formState.value.metadata.name === SUPER_ROLE_NAME) {
|
||||
return `包含所有权限`;
|
||||
}
|
||||
|
||||
const dependenciesCount = JSON.parse(
|
||||
formState.value.metadata.annotations?.[rbacAnnotations.DEPENDENCIES] || "[]"
|
||||
).length;
|
||||
|
||||
return `包含 ${dependenciesCount} 个权限`;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => selectedRoleTemplates.value,
|
||||
(newValue) => {
|
||||
|
@ -97,17 +121,7 @@ onMounted(() => {
|
|||
<p
|
||||
class="mt-1 flex max-w-2xl items-center gap-2 text-sm text-gray-500"
|
||||
>
|
||||
<span
|
||||
>包含
|
||||
{{
|
||||
JSON.parse(
|
||||
formState.metadata.annotations?.[
|
||||
rbacAnnotations.DEPENDENCIES
|
||||
] || "[]"
|
||||
).length
|
||||
}}
|
||||
个权限</span
|
||||
>
|
||||
<span>{{ getRoleCountText }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-t border-gray-200">
|
||||
|
@ -137,10 +151,11 @@ onMounted(() => {
|
|||
>
|
||||
<dt class="text-sm font-medium text-gray-900">类型</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
|
||||
<VTag>系统保留</VTag>
|
||||
<VTag> {{ isSystemReserved ? "系统保留" : "自定义" }} </VTag>
|
||||
</dd>
|
||||
</div>
|
||||
<div
|
||||
v-if="false"
|
||||
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
|
||||
>
|
||||
<dt class="text-sm font-medium text-gray-900">描述</dt>
|
||||
|
@ -217,6 +232,14 @@ onMounted(() => {
|
|||
</div>
|
||||
|
||||
<div v-if="tabActiveId === 'permissions'">
|
||||
<div v-if="isSystemReserved" class="px-4 py-5">
|
||||
<VAlert
|
||||
title="提示"
|
||||
description="系统保留的角色不支持修改,推荐基于此角色创建一个新的角色。"
|
||||
class="w-full sm:w-1/4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<dl class="divide-y divide-gray-100">
|
||||
<div
|
||||
|
@ -259,12 +282,21 @@ onMounted(() => {
|
|||
class="inline-flex w-72 cursor-pointer flex-row items-center gap-4 rounded-base border p-5 hover:border-primary"
|
||||
>
|
||||
<input
|
||||
v-if="!isSuperRole"
|
||||
v-model="selectedRoleTemplates"
|
||||
:value="role.metadata.name"
|
||||
class="h-4 w-4 rounded border-gray-300 text-indigo-600"
|
||||
type="checkbox"
|
||||
:disabled="isSystemReserved"
|
||||
@change="handleRoleTemplateSelect"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
class="h-4 w-4 rounded border-gray-300 text-indigo-600"
|
||||
type="checkbox"
|
||||
checked
|
||||
disabled
|
||||
/>
|
||||
<div class="flex flex-1 flex-col gap-y-3">
|
||||
<span class="font-medium text-gray-900">
|
||||
{{
|
||||
|
@ -301,8 +333,10 @@ onMounted(() => {
|
|||
<VButton
|
||||
:loading="saving"
|
||||
type="secondary"
|
||||
:disabled="isSystemReserved"
|
||||
@click="handleUpdateRole"
|
||||
>保存
|
||||
>
|
||||
保存
|
||||
</VButton>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -33,6 +33,8 @@ import cloneDeep from "lodash.clonedeep";
|
|||
import { apiClient } from "@/utils/api-client";
|
||||
import Fuse from "fuse.js";
|
||||
import { usePermission } from "@/utils/permission";
|
||||
import { roleLabels } from "@/constants/labels";
|
||||
import { SUPER_ROLE_NAME } from "@/constants/constants";
|
||||
|
||||
const { currentUserHasPermission } = usePermission();
|
||||
|
||||
|
@ -63,6 +65,22 @@ const searchResults = computed(() => {
|
|||
return fuse?.search(keyword.value).map((item) => item.item);
|
||||
});
|
||||
|
||||
const isSystemReserved = (role: Role) => {
|
||||
return role.metadata.labels?.[roleLabels.SYSTEM_RESERVED] === "true";
|
||||
};
|
||||
|
||||
const getRoleCountText = (role: Role) => {
|
||||
if (role.metadata.name === SUPER_ROLE_NAME) {
|
||||
return `包含所有权限`;
|
||||
}
|
||||
|
||||
const dependenciesCount = JSON.parse(
|
||||
role.metadata.annotations?.[rbacAnnotations.DEPENDENCIES] || "[]"
|
||||
).length;
|
||||
|
||||
return `包含 ${dependenciesCount} 个权限`;
|
||||
};
|
||||
|
||||
const handleOpenEditingModal = (role: Role) => {
|
||||
selectedRole.value = role;
|
||||
editingModal.value = true;
|
||||
|
@ -73,10 +91,33 @@ const onEditingModalClose = () => {
|
|||
handleFetchRoles();
|
||||
};
|
||||
|
||||
const handleCloneRole = (role: Role) => {
|
||||
const handleCloneRole = async (role: Role) => {
|
||||
const roleToCreate = cloneDeep<Role>(role);
|
||||
roleToCreate.metadata.name = "";
|
||||
roleToCreate.metadata.generateName = "role-";
|
||||
roleToCreate.metadata.creationTimestamp = undefined;
|
||||
|
||||
// 移除系统保留标识
|
||||
delete roleToCreate.metadata.labels?.[roleLabels.SYSTEM_RESERVED];
|
||||
|
||||
// 如果是超级管理员角色,那么需要获取到所有角色模板并填充到表单
|
||||
if (role.metadata.name === SUPER_ROLE_NAME) {
|
||||
const { data } = await apiClient.extension.role.listv1alpha1Role({
|
||||
page: 0,
|
||||
size: 0,
|
||||
labelSelector: [`${roleLabels.TEMPLATE}=true`, "!halo.run/hidden"],
|
||||
});
|
||||
const roleTemplateNames = data.items.map((item) => item.metadata.name);
|
||||
if (roleToCreate.metadata.annotations) {
|
||||
roleToCreate.metadata.annotations[rbacAnnotations.DEPENDENCIES] =
|
||||
JSON.stringify(roleTemplateNames);
|
||||
} else {
|
||||
roleToCreate.metadata.annotations = {
|
||||
[rbacAnnotations.DEPENDENCIES]: JSON.stringify(roleTemplateNames),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
selectedRole.value = roleToCreate;
|
||||
editingModal.value = true;
|
||||
};
|
||||
|
@ -242,15 +283,7 @@ const handleDelete = async (role: Role) => {
|
|||
role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] ||
|
||||
role.metadata.name
|
||||
"
|
||||
:description="`包含
|
||||
${
|
||||
JSON.parse(
|
||||
role.metadata.annotations?.[
|
||||
rbacAnnotations.DEPENDENCIES
|
||||
] || '[]'
|
||||
).length
|
||||
}
|
||||
个权限`"
|
||||
:description="getRoleCountText(role)"
|
||||
:route="{
|
||||
name: 'RoleDetail',
|
||||
params: {
|
||||
|
@ -269,7 +302,9 @@ const handleDelete = async (role: Role) => {
|
|||
<VEntityField v-if="false" description="0 个用户" />
|
||||
<VEntityField>
|
||||
<template #description>
|
||||
<VTag> 系统保留</VTag>
|
||||
<VTag>
|
||||
{{ isSystemReserved(role) ? "系统保留" : "自定义" }}
|
||||
</VTag>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField>
|
||||
|
@ -285,6 +320,7 @@ const handleDelete = async (role: Role) => {
|
|||
#dropdownItems
|
||||
>
|
||||
<VButton
|
||||
v-if="!isSystemReserved(role)"
|
||||
v-close-popper
|
||||
block
|
||||
type="secondary"
|
||||
|
@ -293,6 +329,7 @@ const handleDelete = async (role: Role) => {
|
|||
编辑
|
||||
</VButton>
|
||||
<VButton
|
||||
v-if="!isSystemReserved(role)"
|
||||
v-close-popper
|
||||
block
|
||||
type="danger"
|
||||
|
|
|
@ -97,14 +97,18 @@ export function useRoleForm(): useRoleFormReturn {
|
|||
try {
|
||||
saving.value = true;
|
||||
if (isUpdateMode.value) {
|
||||
await apiClient.extension.role.updatev1alpha1Role({
|
||||
const { data } = await apiClient.extension.role.updatev1alpha1Role({
|
||||
name: formState.value.metadata.name,
|
||||
role: formState.value,
|
||||
});
|
||||
|
||||
formState.value = data;
|
||||
} else {
|
||||
await apiClient.extension.role.createv1alpha1Role({
|
||||
const { data } = await apiClient.extension.role.createv1alpha1Role({
|
||||
role: formState.value,
|
||||
});
|
||||
|
||||
formState.value = data;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
Loading…
Reference in New Issue