mirror of https://github.com/halo-dev/halo
refactor: improve role dependency-related functions and i18n (#5227)
#### What type of PR is this? /area console /area core /milestone 2.12.x #### What this PR does / why we need it: 优化角色模板依赖的相关功能: 1. 修复勾选某个角色模板之后,其下依赖模板没有选中的问题。 2. 修复编辑角色时,模板其下依赖模板没有选中的问题。 3. 修复角色管理列表中,权限数量显示有误的问题。 4. 移除 **允许管理所有文章** 的角色模板,此角色模板与文章管理重复。 5. 优化 i18n。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/5222 #### Special notes for your reviewer: 需要测试上诉问题是否还存在。 #### Does this PR introduce a user-facing change? ```release-note 优化角色模板依赖的相关功能,优化文章相关角色的翻译。 ```pull/5245/head
parent
57fb644173
commit
28ee0bf0e0
|
@ -6,7 +6,7 @@ metadata:
|
|||
halo.run/role-template: "true"
|
||||
annotations:
|
||||
rbac.authorization.halo.run/dependencies: |
|
||||
[ "role-template-view-posts", "role-template-manage-snapshots", "role-template-manage-tags", "role-template-manage-categories" ]
|
||||
[ "role-template-view-posts", "role-template-manage-snapshots", "role-template-manage-tags", "role-template-manage-categories", "role-template-post-author" ]
|
||||
rbac.authorization.halo.run/module: "Posts Management"
|
||||
rbac.authorization.halo.run/display-name: "Post Manage"
|
||||
rbac.authorization.halo.run/ui-permissions: |
|
||||
|
|
|
@ -8,7 +8,7 @@ metadata:
|
|||
# Currently, yaml definition does not support i18n, please see https://github.com/halo-dev/halo/issues/3573
|
||||
rbac.authorization.halo.run/display-name: "文章管理员"
|
||||
rbac.authorization.halo.run/dependencies: |
|
||||
["role-template-post-editor"]
|
||||
["role-template-manage-posts"]
|
||||
rules: [ ]
|
||||
|
||||
---
|
||||
|
@ -16,6 +16,8 @@ apiVersion: v1alpha1
|
|||
kind: "Role"
|
||||
metadata:
|
||||
name: role-template-post-editor
|
||||
# Deprecated, will be removed in the future
|
||||
deletionTimestamp: 2023-12-01T03:36:25.875373Z
|
||||
labels:
|
||||
halo.run/role-template: "true"
|
||||
annotations:
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
VDescriptionItem,
|
||||
} from "@halo-dev/components";
|
||||
import { useRoute } from "vue-router";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import { pluginLabels, roleLabels } from "@/constants/labels";
|
||||
import { rbacAnnotations } from "@/constants/annotations";
|
||||
|
@ -20,6 +20,8 @@ import { SUPER_ROLE_NAME } from "@/constants/constants";
|
|||
import { useI18n } from "vue-i18n";
|
||||
import { formatDatetime } from "@/utils/date";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import type { Role } from "packages/api-client/dist";
|
||||
import { resolveDeepDependencies } from "@/utils/role";
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
@ -58,12 +60,14 @@ const getRoleCountText = computed(() => {
|
|||
return t("core.role.common.text.contains_all_permissions");
|
||||
}
|
||||
|
||||
const dependenciesCount = JSON.parse(
|
||||
formState.value.metadata.annotations?.[rbacAnnotations.DEPENDENCIES] || "[]"
|
||||
).length;
|
||||
const dependencies = new Set<string>(
|
||||
resolveDeepDependencies(formState.value, roleTemplates.value || [])
|
||||
);
|
||||
|
||||
console.log(dependencies);
|
||||
|
||||
return t("core.role.common.text.contains_n_permissions", {
|
||||
count: dependenciesCount,
|
||||
count: dependencies.size || 0,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -77,31 +81,27 @@ watch(
|
|||
}
|
||||
);
|
||||
|
||||
const handleFetchRole = async () => {
|
||||
try {
|
||||
const response = await apiClient.extension.role.getv1alpha1Role({
|
||||
const { refetch } = useQuery<Role>({
|
||||
queryKey: ["role", route.params.name],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiClient.extension.role.getv1alpha1Role({
|
||||
name: route.params.name as string,
|
||||
});
|
||||
formState.value = response.data;
|
||||
selectedRoleTemplates.value = new Set(
|
||||
JSON.parse(
|
||||
response.data.metadata.annotations?.[rbacAnnotations.DEPENDENCIES] ||
|
||||
"[]"
|
||||
)
|
||||
return data;
|
||||
},
|
||||
onSuccess(data) {
|
||||
formState.value = data;
|
||||
selectedRoleTemplates.value = new Set<string>(
|
||||
resolveDeepDependencies(data, roleTemplates.value || [])
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
},
|
||||
enabled: computed(() => !!roleTemplates.value),
|
||||
});
|
||||
|
||||
const handleUpdateRole = async () => {
|
||||
await handleCreateOrUpdate();
|
||||
await handleFetchRole();
|
||||
await refetch();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
handleFetchRole();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VPageHeader :title="$t('core.role.detail.title')">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts" setup>
|
||||
// core libs
|
||||
import { computed, ref, watch } from "vue";
|
||||
import type { Role } from "@halo-dev/api-client";
|
||||
import { computed, ref } from "vue";
|
||||
import type { Role, RoleList } from "@halo-dev/api-client";
|
||||
|
||||
// components
|
||||
import {
|
||||
|
@ -25,9 +25,6 @@ import RoleEditingModal from "./components/RoleEditingModal.vue";
|
|||
import { rbacAnnotations } from "@/constants/annotations";
|
||||
import { formatDatetime } from "@/utils/date";
|
||||
|
||||
// hooks
|
||||
import { useFetchRole } from "@/composables/use-role";
|
||||
|
||||
// libs
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import Fuse from "fuse.js";
|
||||
|
@ -35,6 +32,8 @@ import { usePermission } from "@/utils/permission";
|
|||
import { roleLabels } from "@/constants/labels";
|
||||
import { SUPER_ROLE_NAME } from "@/constants/constants";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { resolveDeepDependencies } from "@/utils/role";
|
||||
|
||||
const { currentUserHasPermission } = usePermission();
|
||||
const { t } = useI18n();
|
||||
|
@ -42,26 +41,65 @@ const { t } = useI18n();
|
|||
const editingModal = ref<boolean>(false);
|
||||
const selectedRole = ref<Role>();
|
||||
|
||||
const { roles, handleFetchRoles, loading } = useFetchRole();
|
||||
|
||||
let fuse: Fuse<Role> | undefined = undefined;
|
||||
|
||||
watch(
|
||||
() => roles.value,
|
||||
(value) => {
|
||||
fuse = new Fuse(value, {
|
||||
keys: ["spec.displayName", "metadata.name"],
|
||||
const { data: roleTemplates } = useQuery({
|
||||
queryKey: ["role-templates"],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiClient.extension.role.listv1alpha1Role({
|
||||
page: 0,
|
||||
size: 0,
|
||||
labelSelector: [`${roleLabels.TEMPLATE}=true`, "!halo.run/hidden"],
|
||||
});
|
||||
return data.items;
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
data: roles,
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useQuery<RoleList>({
|
||||
queryKey: ["roles"],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiClient.extension.role.listv1alpha1Role({
|
||||
page: 0,
|
||||
size: 0,
|
||||
labelSelector: [`!${roleLabels.TEMPLATE}`],
|
||||
});
|
||||
return data;
|
||||
},
|
||||
refetchInterval(data) {
|
||||
const hasDeletingRole = data?.items.some(
|
||||
(item) => !!item.metadata.deletionTimestamp
|
||||
);
|
||||
return hasDeletingRole ? 1000 : false;
|
||||
},
|
||||
onSuccess(data) {
|
||||
fuse = new Fuse(data.items, {
|
||||
keys: [
|
||||
{
|
||||
name: "displayName",
|
||||
getFn: (role) => {
|
||||
return (
|
||||
role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] || ""
|
||||
);
|
||||
},
|
||||
},
|
||||
"metadata.name",
|
||||
],
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.2,
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
enabled: computed(() => !!roleTemplates.value),
|
||||
});
|
||||
|
||||
const keyword = ref("");
|
||||
|
||||
const searchResults = computed(() => {
|
||||
if (!fuse || !keyword.value) {
|
||||
return roles.value;
|
||||
return roles.value?.items || [];
|
||||
}
|
||||
|
||||
return fuse?.search(keyword.value).map((item) => item.item);
|
||||
|
@ -76,12 +114,12 @@ const getRoleCountText = (role: Role) => {
|
|||
return t("core.role.common.text.contains_all_permissions");
|
||||
}
|
||||
|
||||
const dependenciesCount = JSON.parse(
|
||||
role.metadata.annotations?.[rbacAnnotations.DEPENDENCIES] || "[]"
|
||||
).length;
|
||||
const dependencies = new Set<string>(
|
||||
resolveDeepDependencies(role, roleTemplates.value || [])
|
||||
);
|
||||
|
||||
return t("core.role.common.text.contains_n_permissions", {
|
||||
count: dependenciesCount,
|
||||
count: dependencies.size || 0,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -92,7 +130,7 @@ const handleOpenEditingModal = (role: Role) => {
|
|||
|
||||
const onEditingModalClose = () => {
|
||||
selectedRole.value = undefined;
|
||||
handleFetchRoles();
|
||||
refetch();
|
||||
};
|
||||
|
||||
const handleCloneRole = async (role: Role) => {
|
||||
|
@ -152,7 +190,7 @@ const handleDelete = async (role: Role) => {
|
|||
} catch (e) {
|
||||
console.error("Failed to delete role", e);
|
||||
} finally {
|
||||
handleFetchRoles();
|
||||
refetch();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -200,7 +238,7 @@ const handleDelete = async (role: Role) => {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<VLoading v-if="loading" />
|
||||
<VLoading v-if="isLoading" />
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
|
|
|
@ -6,6 +6,7 @@ import { rbacAnnotations } from "@/constants/annotations";
|
|||
import { apiClient } from "@/utils/api-client";
|
||||
import { Toast } from "@halo-dev/components";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { resolveDeepDependencies } from "@/utils/role";
|
||||
|
||||
interface RoleTemplateGroup {
|
||||
module: string | null | undefined;
|
||||
|
@ -38,7 +39,7 @@ interface useRoleFormReturn {
|
|||
initialFormState: Role;
|
||||
saving: Ref<boolean>;
|
||||
isUpdateMode: ComputedRef<boolean>;
|
||||
handleCreateOrUpdate: () => void;
|
||||
handleCreateOrUpdate: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface useRoleTemplateSelectionReturn {
|
||||
|
@ -271,15 +272,15 @@ export function useRoleTemplateSelection(
|
|||
const role = roleTemplates.value?.find(
|
||||
(role) => role.metadata.name === value
|
||||
);
|
||||
const dependencies =
|
||||
role?.metadata.annotations?.[rbacAnnotations.DEPENDENCIES];
|
||||
if (!dependencies) {
|
||||
|
||||
if (!role) {
|
||||
return;
|
||||
}
|
||||
const dependenciesArray = JSON.parse(dependencies);
|
||||
dependenciesArray.forEach((role) => {
|
||||
selectedRoleTemplates.value.add(role);
|
||||
});
|
||||
|
||||
selectedRoleTemplates.value = new Set([
|
||||
role.metadata.name,
|
||||
...resolveDeepDependencies(role, roleTemplates.value || []),
|
||||
]);
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
@ -1322,6 +1322,11 @@ core:
|
|||
role-template-view-tags: Tag View
|
||||
role-template-manage-categories: Category Manage
|
||||
role-template-view-categories: Category View
|
||||
role-template-manage-posts: Post Manage
|
||||
role-template-post-author: Allows you to manage your own posts
|
||||
role-template-post-contributor: Contributions allowed
|
||||
role-template-post-publisher: Allow to publish own posts
|
||||
role-template-post-attachment-manager: Allow images to be uploaded in posts
|
||||
Roles Management: Roles
|
||||
Role Manage: Role Manage
|
||||
Role View: Role View
|
||||
|
@ -1352,9 +1357,8 @@ core:
|
|||
Notification Configuration: Notification Configuration
|
||||
Configure Notifier: Configure Notifier
|
||||
Post Attachment Manager: Allow images to be uploaded in posts
|
||||
Post Author: Contributions allowed
|
||||
Post Contributor: Allows you to manage your own posts
|
||||
Post Editor: Allow management of all posts
|
||||
Post Author: Allows you to manage your own posts
|
||||
Post Contributor: Contributions allowed
|
||||
Post Publisher: Allow to publish own posts
|
||||
components:
|
||||
submit_button:
|
||||
|
|
|
@ -1270,6 +1270,11 @@ core:
|
|||
role-template-view-tags: 标签查看
|
||||
role-template-manage-categories: 分类管理
|
||||
role-template-view-categories: 分类查看
|
||||
role-template-manage-posts: 文章管理
|
||||
role-template-post-author: 允许管理自己的文章
|
||||
role-template-post-contributor: 允许投稿
|
||||
role-template-post-publisher: 允许发布自己的文章
|
||||
role-template-post-attachment-manager: 允许在文章中上传图片
|
||||
Roles Management: 角色
|
||||
Role Manage: 角色管理
|
||||
Role View: 角色查看
|
||||
|
@ -1299,9 +1304,8 @@ core:
|
|||
Cache Manage: 缓存管理
|
||||
Notification Configuration: 通知配置
|
||||
Configure Notifier: 配置通知器
|
||||
Post Editor: 允许管理所有文章
|
||||
Post Contributor: 允许管理自己的文章
|
||||
Post Author: 允许投稿
|
||||
Post Contributor: 允许投稿
|
||||
Post Author: 允许管理自己的文章
|
||||
Post Attachment Manager: 允许在文章中上传图片
|
||||
Post Publisher: 允许发布自己的文章
|
||||
components:
|
||||
|
|
|
@ -1236,6 +1236,11 @@ core:
|
|||
role-template-view-tags: 標籤查看
|
||||
role-template-manage-categories: 分類管理
|
||||
role-template-view-categories: 分類查看
|
||||
role-template-manage-posts: 文章管理
|
||||
role-template-post-author: 允许管理自己的文章
|
||||
role-template-post-contributor: 允许投稿
|
||||
role-template-post-publisher: 允許發布自己的文章
|
||||
role-template-post-attachment-manager: 允許在文章中上傳圖片
|
||||
Roles Management: 角色
|
||||
Role Manage: 角色管理
|
||||
Role View: 角色查看
|
||||
|
@ -1268,7 +1273,6 @@ core:
|
|||
Post Attachment Manager: 允許在文章中上傳圖片
|
||||
Post Author: 允许管理自己的文章
|
||||
Post Contributor: 允许投稿
|
||||
Post Editor: 允许管理所有文章
|
||||
Post Publisher: 允許發布自己的文章
|
||||
components:
|
||||
submit_button:
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { rbacAnnotations } from "@/constants/annotations";
|
||||
import type { Role } from "@halo-dev/api-client";
|
||||
|
||||
export function resolveDeepDependencies(
|
||||
role: Role,
|
||||
roleTemplates: Role[]
|
||||
): string[] {
|
||||
if (!role) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result: string[] = [];
|
||||
|
||||
const dependencies: string[] = JSON.parse(
|
||||
role.metadata.annotations?.[rbacAnnotations.DEPENDENCIES] || "[]"
|
||||
);
|
||||
|
||||
dependencies.forEach((depName) => {
|
||||
result.push(depName);
|
||||
const dep = roleTemplates.find((item) => item.metadata.name === depName);
|
||||
|
||||
if (!dep) {
|
||||
return;
|
||||
}
|
||||
|
||||
resolveDeepDependencies(dep, roleTemplates).forEach((nextDep) =>
|
||||
result.push(nextDep)
|
||||
);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue