mirror of https://github.com/halo-dev/halo-admin
258 lines
6.0 KiB
Vue
258 lines
6.0 KiB
Vue
<script lang="ts" name="UserEditingModal" setup>
|
|
import { computed, onMounted, ref, watch } from "vue";
|
|
import { apiClient } from "@halo-dev/admin-shared";
|
|
import type { Role, User } from "@halo-dev/api-client";
|
|
import {
|
|
IconCodeBoxLine,
|
|
IconEye,
|
|
VButton,
|
|
VCodemirror,
|
|
VModal,
|
|
VSpace,
|
|
} from "@halo-dev/components";
|
|
import { v4 as uuid } from "uuid";
|
|
import { roleLabels } from "@/constants/labels";
|
|
import { rbacAnnotations } from "@/constants/annotations";
|
|
import YAML from "yaml";
|
|
import cloneDeep from "lodash.clonedeep";
|
|
import { useMagicKeys } from "@vueuse/core";
|
|
import { reset, submitForm } from "@formkit/core";
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
visible: boolean;
|
|
user: User | null;
|
|
}>(),
|
|
{
|
|
visible: false,
|
|
user: null,
|
|
}
|
|
);
|
|
|
|
const emit = defineEmits<{
|
|
(event: "update:visible", visible: boolean): void;
|
|
(event: "close"): void;
|
|
}>();
|
|
|
|
interface FormState {
|
|
user: User;
|
|
saving: boolean;
|
|
rawMode: boolean;
|
|
raw: string;
|
|
}
|
|
|
|
const initialFormState: FormState = {
|
|
user: {
|
|
spec: {
|
|
displayName: "",
|
|
avatar: "",
|
|
email: "",
|
|
phone: "",
|
|
password: "",
|
|
bio: "",
|
|
disabled: false,
|
|
loginHistoryLimit: 0,
|
|
},
|
|
apiVersion: "v1alpha1",
|
|
kind: "User",
|
|
metadata: {
|
|
name: uuid(),
|
|
},
|
|
},
|
|
saving: false,
|
|
rawMode: false,
|
|
raw: "",
|
|
};
|
|
|
|
const roles = ref<Role[]>([]);
|
|
const formState = ref<FormState>(cloneDeep(initialFormState));
|
|
const selectedRole = ref("");
|
|
|
|
const isUpdateMode = computed(() => {
|
|
return !!formState.value.user.metadata.creationTimestamp;
|
|
});
|
|
|
|
const creationModalTitle = computed(() => {
|
|
return isUpdateMode.value ? "编辑用户" : "新增用户";
|
|
});
|
|
|
|
const modalWidth = computed(() => {
|
|
return formState.value.rawMode ? 800 : 700;
|
|
});
|
|
|
|
const basicRoles = computed(() => {
|
|
return roles.value.filter(
|
|
(role) => role.metadata?.labels?.[roleLabels.TEMPLATE] !== "true"
|
|
);
|
|
});
|
|
|
|
const { Command_Enter } = useMagicKeys();
|
|
|
|
watch(props, (newVal) => {
|
|
let keyboardWatcher;
|
|
if (newVal.visible) {
|
|
keyboardWatcher = watch(Command_Enter, (v) => {
|
|
if (v) {
|
|
submitForm("user-form");
|
|
}
|
|
});
|
|
} else {
|
|
keyboardWatcher?.unwatch();
|
|
}
|
|
|
|
if (newVal.visible && props.user) {
|
|
formState.value.user = cloneDeep(props.user);
|
|
return;
|
|
}
|
|
formState.value = cloneDeep(initialFormState);
|
|
reset("user-form");
|
|
});
|
|
|
|
const handleFetchRoles = async () => {
|
|
try {
|
|
const { data } = await apiClient.extension.role.listv1alpha1Role();
|
|
roles.value = data.items;
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
|
|
const handleVisibleChange = (visible: boolean) => {
|
|
emit("update:visible", visible);
|
|
if (!visible) {
|
|
emit("close");
|
|
}
|
|
};
|
|
|
|
const handleCreateUser = async () => {
|
|
try {
|
|
formState.value.saving = true;
|
|
let user: User;
|
|
|
|
if (isUpdateMode.value) {
|
|
const response = await apiClient.extension.user.updatev1alpha1User(
|
|
formState.value.user.metadata.name,
|
|
formState.value.user
|
|
);
|
|
user = response.data;
|
|
} else {
|
|
const response = await apiClient.extension.user.createv1alpha1User(
|
|
formState.value.user
|
|
);
|
|
user = response.data;
|
|
}
|
|
|
|
if (selectedRole.value) {
|
|
await apiClient.user.grantPermission(user.metadata.name, {
|
|
// @ts-ignore
|
|
roles: [selectedRole.value],
|
|
});
|
|
}
|
|
|
|
handleVisibleChange(false);
|
|
} catch (e) {
|
|
console.error(e);
|
|
} finally {
|
|
formState.value.saving = false;
|
|
}
|
|
};
|
|
|
|
const handleRawModeChange = () => {
|
|
formState.value.rawMode = !formState.value.rawMode;
|
|
|
|
if (formState.value.rawMode) {
|
|
formState.value.raw = YAML.stringify(formState.value.user);
|
|
} else {
|
|
formState.value.user = YAML.parse(formState.value.raw);
|
|
}
|
|
};
|
|
|
|
onMounted(handleFetchRoles);
|
|
</script>
|
|
<template>
|
|
<VModal
|
|
:title="creationModalTitle"
|
|
:visible="visible"
|
|
:width="modalWidth"
|
|
@update:visible="handleVisibleChange"
|
|
>
|
|
<template #actions>
|
|
<div class="modal-header-action" @click="handleRawModeChange">
|
|
<IconCodeBoxLine v-if="!formState.rawMode" />
|
|
<IconEye v-else />
|
|
</div>
|
|
</template>
|
|
|
|
<VCodemirror
|
|
v-show="formState.rawMode"
|
|
v-model="formState.raw"
|
|
height="50vh"
|
|
language="yaml"
|
|
/>
|
|
|
|
<div v-show="!formState.rawMode">
|
|
<FormKit id="user-form" type="form" @submit="handleCreateUser">
|
|
<FormKit
|
|
v-model="formState.user.metadata.name"
|
|
:disabled="isUpdateMode"
|
|
label="用户名"
|
|
type="text"
|
|
validation="required"
|
|
></FormKit>
|
|
<FormKit
|
|
v-model="formState.user.spec.displayName"
|
|
label="显示名称"
|
|
type="text"
|
|
validation="required"
|
|
></FormKit>
|
|
<FormKit
|
|
v-model="formState.user.spec.email"
|
|
label="电子邮箱"
|
|
type="email"
|
|
validation="required"
|
|
></FormKit>
|
|
<FormKit
|
|
v-model="selectedRole"
|
|
:options="
|
|
basicRoles.map((role:Role) => {
|
|
return {
|
|
label: role.metadata?.annotations?.[rbacAnnotations.DISPLAY_NAME] || role.metadata.name,
|
|
value: role.metadata?.name,
|
|
};
|
|
})
|
|
"
|
|
label="角色"
|
|
type="select"
|
|
></FormKit>
|
|
<FormKit
|
|
v-model="formState.user.spec.phone"
|
|
label="手机号"
|
|
type="text"
|
|
></FormKit>
|
|
<FormKit
|
|
v-model="formState.user.spec.avatar"
|
|
label="头像"
|
|
type="text"
|
|
></FormKit>
|
|
<FormKit
|
|
v-model="formState.user.spec.bio"
|
|
label="描述"
|
|
type="textarea"
|
|
></FormKit>
|
|
</FormKit>
|
|
</div>
|
|
<template #footer>
|
|
<VSpace>
|
|
<VButton
|
|
:loading="formState.saving"
|
|
type="secondary"
|
|
@click="$formkit.submit('user-form')"
|
|
>
|
|
保存 ⌘ + ↵
|
|
</VButton>
|
|
<VButton @click="handleVisibleChange(false)">取消 Esc</VButton>
|
|
</VSpace>
|
|
</template>
|
|
</VModal>
|
|
</template>
|