mirror of https://github.com/halo-dev/halo
refactor: grant permissions to user
Signed-off-by: Ryan Wang <i@ryanc.cc>pull/3445/head
parent
a77fc3b2a5
commit
0e2f0599fd
|
@ -18,6 +18,7 @@ import {
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import UserEditingModal from "./components/UserEditingModal.vue";
|
import UserEditingModal from "./components/UserEditingModal.vue";
|
||||||
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue";
|
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue";
|
||||||
|
import GrantPermissionModal from "./components/GrantPermissionModal.vue";
|
||||||
import { inject, onMounted, ref, watch } from "vue";
|
import { inject, onMounted, ref, watch } from "vue";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
import type { User, UserList } from "@halo-dev/api-client";
|
import type { User, UserList } from "@halo-dev/api-client";
|
||||||
|
@ -30,6 +31,8 @@ const dialog = useDialog();
|
||||||
const checkedAll = ref(false);
|
const checkedAll = ref(false);
|
||||||
const editingModal = ref<boolean>(false);
|
const editingModal = ref<boolean>(false);
|
||||||
const passwordChangeModal = ref<boolean>(false);
|
const passwordChangeModal = ref<boolean>(false);
|
||||||
|
const grantPermissionModal = ref<boolean>(false);
|
||||||
|
|
||||||
const users = ref<UserList>({
|
const users = ref<UserList>({
|
||||||
page: 1,
|
page: 1,
|
||||||
size: 10,
|
size: 10,
|
||||||
|
@ -41,7 +44,7 @@ const users = ref<UserList>({
|
||||||
hasPrevious: false,
|
hasPrevious: false,
|
||||||
});
|
});
|
||||||
const selectedUserNames = ref<string[]>([]);
|
const selectedUserNames = ref<string[]>([]);
|
||||||
const selectedUser = ref<User | null>(null);
|
const selectedUser = ref<User>();
|
||||||
const currentUser = inject<User>("currentUser");
|
const currentUser = inject<User>("currentUser");
|
||||||
|
|
||||||
const handleFetchUsers = async () => {
|
const handleFetchUsers = async () => {
|
||||||
|
@ -54,7 +57,7 @@ const handleFetchUsers = async () => {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
selectedUser.value = null;
|
selectedUser.value = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,6 +152,11 @@ const handleOpenPasswordChangeModal = (user: User) => {
|
||||||
passwordChangeModal.value = true;
|
passwordChangeModal.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenGrantPermissionModal = (user: User) => {
|
||||||
|
selectedUser.value = user;
|
||||||
|
grantPermissionModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
const getRoles = (user: User) => {
|
const getRoles = (user: User) => {
|
||||||
return JSON.parse(
|
return JSON.parse(
|
||||||
user.metadata.annotations?.[rbacAnnotations.ROLE_NAMES] || "[]"
|
user.metadata.annotations?.[rbacAnnotations.ROLE_NAMES] || "[]"
|
||||||
|
@ -184,6 +192,12 @@ onMounted(() => {
|
||||||
@close="handleFetchUsers"
|
@close="handleFetchUsers"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<GrantPermissionModal
|
||||||
|
v-model:visible="grantPermissionModal"
|
||||||
|
:user="selectedUser"
|
||||||
|
@close="handleFetchUsers"
|
||||||
|
/>
|
||||||
|
|
||||||
<VPageHeader title="用户">
|
<VPageHeader title="用户">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<IconUserSettings class="mr-2 self-center" />
|
<IconUserSettings class="mr-2 self-center" />
|
||||||
|
@ -419,6 +433,15 @@ onMounted(() => {
|
||||||
>
|
>
|
||||||
修改密码
|
修改密码
|
||||||
</VButton>
|
</VButton>
|
||||||
|
<VButton
|
||||||
|
v-if="currentUser?.metadata.name !== user.metadata.name"
|
||||||
|
v-close-popper
|
||||||
|
v-permission="['system:users:manage']"
|
||||||
|
block
|
||||||
|
@click="handleOpenGrantPermissionModal(user)"
|
||||||
|
>
|
||||||
|
分配角色
|
||||||
|
</VButton>
|
||||||
<VButton
|
<VButton
|
||||||
v-if="currentUser?.metadata.name !== user.metadata.name"
|
v-if="currentUser?.metadata.name !== user.metadata.name"
|
||||||
v-close-popper
|
v-close-popper
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { rbacAnnotations } from "@/constants/annotations";
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import type { FormKitOptionsList } from "@formkit/inputs";
|
||||||
|
import type { User } from "@halo-dev/api-client";
|
||||||
|
import { VModal, VSpace, VButton } from "@halo-dev/components";
|
||||||
|
import SubmitButton from "@/components/button/SubmitButton.vue";
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { useFetchRole } from "../../roles/composables/use-role";
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
user?: User;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
visible: false,
|
||||||
|
user: undefined,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: "update:visible", visible: boolean): void;
|
||||||
|
(event: "close"): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const selectedRole = ref("");
|
||||||
|
const saving = ref(false);
|
||||||
|
|
||||||
|
const { roles } = useFetchRole();
|
||||||
|
const rolesMap = computed<FormKitOptionsList>(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: "请选择角色",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
...roles.value.map((role) => {
|
||||||
|
return {
|
||||||
|
label:
|
||||||
|
role.metadata?.annotations?.[rbacAnnotations.DISPLAY_NAME] ||
|
||||||
|
role.metadata.name,
|
||||||
|
value: role.metadata?.name,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleGrantPermission = async () => {
|
||||||
|
try {
|
||||||
|
saving.value = true;
|
||||||
|
await apiClient.user.grantPermission({
|
||||||
|
name: props.user.metadata.name,
|
||||||
|
grantRequest: {
|
||||||
|
roles: [selectedRole.value],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
onVisibleChange(false);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Failed to grant permission to user", error);
|
||||||
|
} finally {
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onVisibleChange = (visible: boolean) => {
|
||||||
|
emit("update:visible", visible);
|
||||||
|
if (!visible) {
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VModal
|
||||||
|
title="分类角色"
|
||||||
|
:visible="visible"
|
||||||
|
:width="500"
|
||||||
|
@update:visible="onVisibleChange"
|
||||||
|
>
|
||||||
|
<FormKit
|
||||||
|
id="grant-permission-form"
|
||||||
|
name="grant-permission-form"
|
||||||
|
:config="{ validationVisibility: 'submit' }"
|
||||||
|
type="form"
|
||||||
|
@submit="handleGrantPermission"
|
||||||
|
>
|
||||||
|
<FormKit
|
||||||
|
v-model="selectedRole"
|
||||||
|
:options="rolesMap"
|
||||||
|
label="角色"
|
||||||
|
type="select"
|
||||||
|
></FormKit>
|
||||||
|
</FormKit>
|
||||||
|
<template #footer>
|
||||||
|
<VSpace>
|
||||||
|
<SubmitButton
|
||||||
|
v-if="visible"
|
||||||
|
:loading="saving"
|
||||||
|
type="secondary"
|
||||||
|
@submit="$formkit.submit('grant-permission-form')"
|
||||||
|
>
|
||||||
|
</SubmitButton>
|
||||||
|
<VButton @click="onVisibleChange(false)">取消 Esc</VButton>
|
||||||
|
</VSpace>
|
||||||
|
</template>
|
||||||
|
</VModal>
|
||||||
|
</template>
|
|
@ -20,22 +20,17 @@ import YAML from "yaml";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import { reset } from "@formkit/core";
|
import { reset } from "@formkit/core";
|
||||||
|
|
||||||
// constants
|
|
||||||
import { rbacAnnotations } from "@/constants/annotations";
|
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useFetchRole } from "@/modules/system/roles/composables/use-role";
|
|
||||||
import type { FormKitOptionsList } from "@formkit/inputs";
|
|
||||||
import { setFocus } from "@/formkit/utils/focus";
|
import { setFocus } from "@/formkit/utils/focus";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
user: User | null;
|
user?: User;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
visible: false,
|
visible: false,
|
||||||
user: null,
|
user: undefined,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -66,7 +61,6 @@ const formState = ref<User>(cloneDeep(initialFormState));
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
const rawMode = ref(false);
|
const rawMode = ref(false);
|
||||||
const raw = ref("");
|
const raw = ref("");
|
||||||
const selectedRole = ref("");
|
|
||||||
|
|
||||||
const isUpdateMode = computed(() => {
|
const isUpdateMode = computed(() => {
|
||||||
return !!formState.value.metadata.creationTimestamp;
|
return !!formState.value.metadata.creationTimestamp;
|
||||||
|
@ -80,18 +74,6 @@ const modalWidth = computed(() => {
|
||||||
return rawMode.value ? 800 : 700;
|
return rawMode.value ? 800 : 700;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { roles } = useFetchRole();
|
|
||||||
const rolesMap = computed<FormKitOptionsList>(() => {
|
|
||||||
return roles.value.map((role) => {
|
|
||||||
return {
|
|
||||||
label:
|
|
||||||
role.metadata?.annotations?.[rbacAnnotations.DISPLAY_NAME] ||
|
|
||||||
role.metadata.name,
|
|
||||||
value: role.metadata?.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
(visible) => {
|
(visible) => {
|
||||||
|
@ -129,33 +111,20 @@ const handleResetForm = () => {
|
||||||
const handleCreateUser = async () => {
|
const handleCreateUser = async () => {
|
||||||
try {
|
try {
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
let user: User;
|
|
||||||
|
|
||||||
if (isUpdateMode.value) {
|
if (isUpdateMode.value) {
|
||||||
const response = await apiClient.extension.user.updatev1alpha1User({
|
await apiClient.extension.user.updatev1alpha1User({
|
||||||
name: formState.value.metadata.name,
|
name: formState.value.metadata.name,
|
||||||
user: formState.value,
|
user: formState.value,
|
||||||
});
|
});
|
||||||
user = response.data;
|
|
||||||
} else {
|
} else {
|
||||||
const response = await apiClient.extension.user.createv1alpha1User({
|
await apiClient.extension.user.createv1alpha1User({
|
||||||
user: formState.value,
|
user: formState.value,
|
||||||
});
|
});
|
||||||
user = response.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedRole.value) {
|
|
||||||
await apiClient.user.grantPermission({
|
|
||||||
name: user.metadata.name,
|
|
||||||
grantRequest: {
|
|
||||||
roles: [selectedRole.value],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChange(false);
|
onVisibleChange(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error("Failed to create or update user", e);
|
||||||
} finally {
|
} finally {
|
||||||
saving.value = false;
|
saving.value = false;
|
||||||
}
|
}
|
||||||
|
@ -219,12 +188,6 @@ const handleRawModeChange = () => {
|
||||||
name="email"
|
name="email"
|
||||||
validation="required"
|
validation="required"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
|
||||||
v-model="selectedRole"
|
|
||||||
:options="rolesMap"
|
|
||||||
label="角色"
|
|
||||||
type="select"
|
|
||||||
></FormKit>
|
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.phone"
|
v-model="formState.spec.phone"
|
||||||
label="手机号"
|
label="手机号"
|
||||||
|
|
|
@ -11,11 +11,11 @@ import { setFocus } from "@/formkit/utils/focus";
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
user: User | null;
|
user?: User;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
visible: false,
|
visible: false,
|
||||||
user: null,
|
user: undefined,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue