refactor: user detail page

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/603/head
Ryan Wang 2022-08-23 17:33:45 +08:00
parent fd56f24b1f
commit dae208f543
6 changed files with 50 additions and 146 deletions

View File

@ -1,5 +1,4 @@
export { default as BlankLayout } from "./BlankLayout.vue"; export { default as BlankLayout } from "./BlankLayout.vue";
export { default as BasicLayout } from "./BasicLayout.vue"; export { default as BasicLayout } from "./BasicLayout.vue";
export { default as SystemSettingsLayout } from "./SystemSettingsLayout.vue"; export { default as SystemSettingsLayout } from "./SystemSettingsLayout.vue";
export { default as UserProfileLayout } from "./UserProfileLayout.vue";
export { default as PluginLayout } from "./PluginLayout.vue"; export { default as PluginLayout } from "./PluginLayout.vue";

View File

@ -4,7 +4,7 @@ const textClassification = {
inner: inner:
"inline-flex items-center w-full relative box-border border border-gray-300 formkit-invalid:border-red-500 h-9 rounded-base overflow-hidden focus-within:border-primary mt-2 sm:mt-0", "inline-flex items-center w-full relative box-border border border-gray-300 formkit-invalid:border-red-500 h-9 rounded-base overflow-hidden focus-within:border-primary mt-2 sm:mt-0",
input: input:
"outline-0 bg-white antialiased resize-none w-full text-black block transition-all appearance-none h-9 px-3 text-sm", "outline-0 bg-white antialiased resize-none w-full text-black block transition-all appearance-none h-full px-3 text-sm",
}; };
const boxClassification = { const boxClassification = {
@ -74,8 +74,10 @@ const theme: Record<string, Record<string, string>> = {
"datetime-local": textClassification, "datetime-local": textClassification,
textarea: { textarea: {
...textClassification, ...textClassification,
inner:
"inline-flex items-center w-full relative box-border border border-gray-300 formkit-invalid:border-red-500 h-32 rounded-base overflow-hidden focus-within:border-primary mt-2 sm:mt-0",
input: input:
"outline-0 bg-white antialiased w-full text-black block transition-all appearance-none h-32 px-3 py-2 text-sm", "outline-0 bg-white antialiased w-full text-black block transition-all appearance-none h-full px-3 py-2 text-sm",
}, },
}; };

View File

@ -1,72 +0,0 @@
<script lang="ts" setup>
import { VButton } from "@halo-dev/components";
import { inject, ref } from "vue";
import type { Ref } from "vue";
import { apiClient } from "@halo-dev/admin-shared";
import type { User } from "@halo-dev/api-client";
interface FormState {
state: {
password: string;
password_confirm?: string;
};
submitting: boolean;
}
const user = inject<Ref<User>>("user");
const currentUser = inject<User>("currentUser");
const formState = ref<FormState>({
state: {
password: "",
password_confirm: "",
},
submitting: false,
});
const handleChangePassword = async () => {
try {
formState.value.submitting = true;
if (user?.value.metadata.name === currentUser?.metadata.name) {
await apiClient.user.changePassword("-", formState.value.state);
}
} catch (e) {
console.error(e);
} finally {
formState.value.submitting = false;
}
};
</script>
<template>
<FormKit
id="password-form"
v-model="formState.state"
:actions="false"
type="form"
@submit="handleChangePassword"
>
<FormKit
label="新密码"
name="password"
type="password"
validation="required"
></FormKit>
<FormKit
label="确认密码"
name="password_confirm"
type="password"
validation="required|confirm"
></FormKit>
</FormKit>
<div class="pt-5">
<div class="flex justify-start">
<VButton
:loading="formState.submitting"
type="secondary"
@click="$formkit.submit('password-form')"
>修改密码
</VButton>
</div>
</div>
</template>

View File

@ -1,37 +0,0 @@
<script lang="ts" setup>
import { VButton } from "@halo-dev/components";
import { inject } from "vue";
import type { Ref } from "vue";
import type { User } from "@halo-dev/api-client";
const user = inject<Ref<User>>("user");
</script>
<template>
<FormKit v-if="user" id="user-form" :actions="false" type="form">
<FormKit
v-model="user.metadata.name"
label="用户名"
type="text"
validation="required"
></FormKit>
<FormKit
v-model="user.spec.displayName"
label="显示名称"
type="text"
validation="required"
></FormKit>
<FormKit
v-model="user.spec.email"
label="电子邮箱"
type="email"
validation="required"
></FormKit>
<FormKit v-model="user.spec.bio" label="描述" type="textarea"></FormKit>
</FormKit>
<div class="pt-5">
<div class="flex justify-start">
<VButton type="secondary">保存</VButton>
</div>
</div>
</template>

View File

@ -1,10 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { BasicLayout } from "../layouts"; import { apiClient, BasicLayout } from "@halo-dev/admin-shared";
import { IconUpload, VButton, VTabbar } from "@halo-dev/components"; import { IconUpload, VButton, VSpace, VTabbar } from "@halo-dev/components";
import { onMounted, provide, ref, watch } from "vue"; import { onMounted, provide, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { apiClient } from "../utils/api-client";
import type { User } from "@halo-dev/api-client"; import type { User } from "@halo-dev/api-client";
import UserEditingModal from "../components/UserEditingModal.vue";
import UserPasswordChangeModal from "../components/UserPasswordChangeModal.vue";
const tabs = [ const tabs = [
{ {
@ -17,19 +18,11 @@ const tabs = [
label: "个人令牌", label: "个人令牌",
routeName: "PersonalAccessTokens", routeName: "PersonalAccessTokens",
}, },
{
id: "profile-modification",
label: "资料修改",
routeName: "ProfileModification",
},
{
id: "password-change",
label: "密码修改",
routeName: "PasswordChange",
},
]; ];
const user = ref<User>(); const user = ref<User>();
const editingModal = ref(false);
const passwordChangeModal = ref(false);
const { params } = useRoute(); const { params } = useRoute();
@ -75,16 +68,26 @@ const handleTabChange = (id: string) => {
</script> </script>
<template> <template>
<BasicLayout> <BasicLayout>
<UserEditingModal
v-model:visible="editingModal"
:user="user"
@close="handleFetchUser"
/>
<UserPasswordChangeModal
v-model:visible="passwordChangeModal"
:user="user"
@close="handleFetchUser"
/>
<header class="bg-white"> <header class="bg-white">
<div class="h-48 bg-gradient-to-r from-gray-800 to-red-500"></div> <div class="h-48 bg-gradient-to-r from-gray-800 to-red-500"></div>
<div class="px-4 sm:px-6 lg:px-8"> <div class="px-4 sm:px-6 lg:px-8">
<div class="-mt-12 flex items-end space-x-5 sm:-mt-16"> <div class="-mt-12 flex items-end space-x-5 sm:-mt-16">
<div class="flex"> <div class="flex">
<div class="bg-white h-24 w-24 sm:h-32 sm:w-32"> <div class="h-24 w-24 sm:h-32 sm:w-32">
<img <img
:src="user?.spec?.avatar" :src="user?.spec?.avatar"
alt="Avatar" alt="Avatar"
class="h-full w-full rounded-full ring-4 ring-white drop-shadow-lg" class="h-full w-full rounded-full bg-white ring-4 ring-white drop-shadow-lg"
/> />
</div> </div>
</div> </div>
@ -95,11 +98,35 @@ const handleTabChange = (id: string) => {
<h1 class="truncate text-xl font-bold text-gray-900"> <h1 class="truncate text-xl font-bold text-gray-900">
<span class="mr-1">{{ user?.spec?.displayName }}</span> <span class="mr-1">{{ user?.spec?.displayName }}</span>
</h1> </h1>
<h2 class="text-gray-600">@{{ user?.metadata.name }}</h2>
</div> </div>
<div <div
class="justify-stretch mt-6 hidden flex-col space-y-3 sm:flex-row sm:space-y-0 sm:space-x-4 md:flex" class="justify-stretch mt-6 hidden flex-col space-y-3 sm:flex-row sm:space-y-0 sm:space-x-4 md:flex"
> >
<VButton type="default">退出登录</VButton> <FloatingDropdown>
<VButton type="default">编辑</VButton>
<template #popper>
<div class="w-48 p-2">
<VSpace class="w-full" direction="column">
<VButton
v-close-popper
block
type="secondary"
@click="editingModal = true"
>
修改资料
</VButton>
<VButton
v-close-popper
block
@click="passwordChangeModal = true"
>
修改密码
</VButton>
</VSpace>
</div>
</template>
</FloatingDropdown>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,13 +1,7 @@
import { import { BasicLayout, BlankLayout, definePlugin } from "@halo-dev/admin-shared";
BasicLayout, import UserProfileLayout from "./layouts/UserProfileLayout.vue";
BlankLayout,
definePlugin,
UserProfileLayout,
} from "@halo-dev/admin-shared";
import UserList from "./UserList.vue"; import UserList from "./UserList.vue";
import UserDetail from "./UserDetail.vue"; import UserDetail from "./UserDetail.vue";
import ProfileModification from "./ProfileModification.vue";
import PasswordChange from "./PasswordChange.vue";
import PersonalAccessTokens from "./PersonalAccessTokens.vue"; import PersonalAccessTokens from "./PersonalAccessTokens.vue";
import Login from "./Login.vue"; import Login from "./Login.vue";
import { IconUserSettings } from "@halo-dev/components"; import { IconUserSettings } from "@halo-dev/components";
@ -42,22 +36,13 @@ export default definePlugin({
{ {
path: ":name", path: ":name",
component: UserProfileLayout, component: UserProfileLayout,
name: "User",
children: [ children: [
{ {
path: "detail", path: "detail",
name: "UserDetail", name: "UserDetail",
component: UserDetail, component: UserDetail,
}, },
{
path: "profile-modification",
name: "ProfileModification",
component: ProfileModification,
},
{
path: "password-change",
name: "PasswordChange",
component: PasswordChange,
},
{ {
path: "tokens", path: "tokens",
name: "PersonalAccessTokens", name: "PersonalAccessTokens",