zhouhongshuo ca9430f82c 初始化
2024-08-25 23:46:05 +08:00

299 lines
9.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import { useRoleForm, useRoleTemplateSelection } from "@/composables/use-role";
import { rbacAnnotations } from "@/constants/annotations";
import { SUPER_ROLE_NAME } from "@/constants/constants";
import { pluginLabels, roleLabels } from "@/constants/labels";
import { formatDatetime } from "@/utils/date";
import { resolveDeepDependencies } from "@/utils/role";
import type { Role } from "@halo-dev/api-client";
import { coreApiClient } from "@halo-dev/api-client";
import {
IconShieldUser,
VAlert,
VButton,
VCard,
VDescription,
VDescriptionItem,
VPageHeader,
VTabbar,
VTag,
} from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useRoute } from "vue-router";
const route = useRoute();
const { t } = useI18n();
const tabActiveId = ref("detail");
const { data: roleTemplates } = useQuery({
queryKey: ["role-templates"],
queryFn: async () => {
const { data } = await coreApiClient.role.listRole({
page: 0,
size: 0,
labelSelector: [`${roleLabels.TEMPLATE}=true`, "!halo.run/hidden"],
});
return data.items;
},
});
const { roleTemplateGroups, handleRoleTemplateSelect, selectedRoleTemplates } =
useRoleTemplateSelection(roleTemplates);
const { formState, isSubmitting, handleCreateOrUpdate } = useRoleForm();
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 t("core.role.common.text.contains_all_permissions");
}
const dependencies = new Set<string>(
resolveDeepDependencies(formState.value, roleTemplates.value || [])
);
return t("core.role.common.text.contains_n_permissions", {
count: dependencies.size || 0,
});
});
watch(
() => selectedRoleTemplates.value,
(newValue) => {
if (formState.value.metadata.annotations) {
formState.value.metadata.annotations[rbacAnnotations.DEPENDENCIES] =
JSON.stringify(Array.from(newValue));
}
}
);
const { refetch } = useQuery<Role>({
queryKey: ["role", route.params.name],
queryFn: async () => {
const { data } = await coreApiClient.role.getRole({
name: route.params.name as string,
});
return data;
},
onSuccess(data) {
formState.value = data;
selectedRoleTemplates.value = new Set<string>(
resolveDeepDependencies(data, roleTemplates.value || [])
);
},
enabled: computed(() => !!roleTemplates.value),
});
const handleUpdateRole = async () => {
await handleCreateOrUpdate();
await refetch();
};
</script>
<template>
<VPageHeader :title="$t('core.role.detail.title')">
<template #icon>
<IconShieldUser class="mr-2 self-center" />
</template>
</VPageHeader>
<div class="m-0 md:m-4">
<VCard :body-class="['!p-0']">
<template #header>
<VTabbar
v-model:active-id="tabActiveId"
:items="[
{ id: 'detail', label: $t('core.role.detail.tabs.detail') },
{
id: 'permissions',
label: $t('core.role.detail.tabs.permissions'),
},
]"
class="w-full !rounded-none"
type="outline"
></VTabbar>
</template>
<div v-if="tabActiveId === 'detail'">
<div class="px-4 py-4 sm:px-6">
<h3 class="text-lg font-medium leading-6 text-gray-900">
{{ $t("core.role.detail.header.title") }}
</h3>
<p
class="mt-1 flex max-w-2xl items-center gap-2 text-sm text-gray-500"
>
<span>{{ getRoleCountText }}</span>
</p>
</div>
<div class="border-t border-gray-200">
<VDescription>
<VDescriptionItem
:label="$t('core.role.detail.fields.display_name')"
>
{{
formState.metadata?.annotations?.[
rbacAnnotations.DISPLAY_NAME
] || formState.metadata?.name
}}
</VDescriptionItem>
<VDescriptionItem
:label="$t('core.role.detail.fields.name')"
:content="formState.metadata?.name"
/>
<VDescriptionItem :label="$t('core.role.detail.fields.type')">
<VTag>
{{
isSystemReserved
? t("core.role.common.text.system_reserved")
: t("core.role.common.text.custom")
}}
</VTag>
</VDescriptionItem>
<VDescriptionItem
:label="$t('core.role.detail.fields.creation_time')"
:content="formatDatetime(formState.metadata.creationTimestamp)"
/>
</VDescription>
</div>
</div>
<div v-if="tabActiveId === 'permissions'">
<div v-if="isSystemReserved" class="px-4 py-5">
<VAlert
:title="$t('core.common.text.tip')"
:description="
$t(
'core.role.permissions_detail.system_reserved_alert.description'
)
"
class="w-full sm:w-1/4"
:closable="false"
/>
</div>
<div>
<dl class="divide-y divide-gray-100">
<div
v-for="(group, groupIndex) in roleTemplateGroups"
:key="groupIndex"
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">
<div>
{{ $t(`core.rbac.${group.module}`, group.module as string) }}
</div>
<div
v-if="
group.roles.length &&
group.roles[0].metadata.labels?.[pluginLabels.NAME]
"
class="mt-3 text-xs text-gray-500"
>
<i18n-t
keypath="core.role.common.text.provided_by_plugin"
tag="div"
>
<template #plugin>
<RouterLink
:to="{
name: 'PluginDetail',
params: {
name: group.roles[0].metadata.labels?.[
pluginLabels.NAME
],
},
}"
class="hover:text-blue-600"
>
{{
group.roles[0].metadata.labels?.[pluginLabels.NAME]
}}
</RouterLink>
</template>
</i18n-t>
</div>
</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<ul class="space-y-2">
<li v-for="(role, index) in group.roles" :key="index">
<label
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"
type="checkbox"
:disabled="isSystemReserved"
@change="handleRoleTemplateSelect"
/>
<input v-else type="checkbox" checked disabled />
<div class="flex flex-1 flex-col gap-y-3">
<span class="font-medium text-gray-900">
{{
$t(
`core.rbac.${
role.metadata.annotations?.[
rbacAnnotations.DISPLAY_NAME
]
}`,
role.metadata.annotations?.[
rbacAnnotations.DISPLAY_NAME
] as string
)
}}
</span>
<span
v-if="
role.metadata.annotations?.[
rbacAnnotations.DEPENDENCIES
]
"
class="text-xs text-gray-400"
>
{{
$t("core.role.common.text.dependent_on", {
roles: JSON.parse(
role.metadata.annotations?.[
rbacAnnotations.DEPENDENCIES
]
)
.map((item: string) =>
$t(`core.rbac.${item}`, item as string)
)
.join(""),
})
}}
</span>
</div>
</label>
</li>
</ul>
</dd>
</div>
</dl>
<div v-permission="['system:roles:manage']" class="p-4">
<VButton
:loading="isSubmitting"
type="secondary"
:disabled="isSystemReserved"
@click="handleUpdateRole"
>
{{ $t("core.common.buttons.save") }}
</VButton>
</div>
</div>
</div>
</VCard>
</div>
</template>